diff --git a/db_facts/lpass.py b/db_facts/lpass.py index 00a11f9..a74f237 100644 --- a/db_facts/lpass.py +++ b/db_facts/lpass.py @@ -1,3 +1,10 @@ +# ************************************************ +# *** ATTENTION *** THIS DOES NOT USE LASTPASS *** +# ************************************************ +# BlueLabs is currently transitioning off lastpass and onto 1password. +# Even though this file says "lpass", all underlying calls to the +# lpass CLI have been replaced with calls to the 1password CLI. + from subprocess import check_output from .db_facts_types import LastPassUsernamePassword, LastPassAWSIAM from .db_type import canonicalize_db_type, db_protocol @@ -19,20 +26,22 @@ def pull_lastpass_aws_iam(lastpass_entry_name: str) -> LastPassAWSIAM: def lpass_field(name: str, field: str) -> str: - if field == 'notes': - field_arg = '--notes' - elif field == 'username': - field_arg = '--username' - elif field == 'password': - field_arg = '--password' - elif field == 'url': - field_arg = '--url' - else: - field_arg = '--field=' + field - raw_output = check_output(['lpass', - 'show', - field_arg, - name]) + # *** ATTENTION *** THIS DOES NOT USE LASTPASS *** + + # This used to use the lastpass-cli to pull credentials. But we've moved + # from lastpass to 1password. This command retrieves the fields in the + # same format from 1password instead. + + # Note this won't work for the way 1password stores notes and URLs, which + # is different from lpass. But as of now db-facts doesn't ever rely on + # these fields. + if field == 'notes' or field == 'url': + raise NotImplementedError( + 'Cannot retrieve notes or URL fields from 1password') + + raw_output = check_output( + ['op', 'item', 'get', name, '--field', f'label={field}']) + return raw_output.decode('utf-8').rstrip('\n') diff --git a/tests/test_lpass.py b/tests/test_lpass.py index 5dd3a45..564ba39 100644 --- a/tests/test_lpass.py +++ b/tests/test_lpass.py @@ -1,64 +1,65 @@ +# ************************************************ +# *** ATTENTION *** THIS DOES NOT USE LASTPASS *** +# ************************************************ +# BlueLabs is currently transitioning off lastpass and onto 1password. +# Even though this file says "lpass", all underlying calls to the +# lpass CLI have been replaced with calls to the 1password CLI. + import unittest from unittest.mock import patch from db_facts import lpass class TestLPass(unittest.TestCase): - @patch('db_facts.lpass.check_output') - def test_lpass_field_notes(self, mock_check_output): - mock_check_output.return_value = "fakenotes\n".encode("utf-8") - out = lpass.lpass_field('my_name', 'notes') - mock_check_output.\ - assert_called_with(['lpass', 'show', '--notes', 'my_name']) - assert out == "fakenotes" + def test_lpass_field_url_raises(self): + with self.assertRaises(NotImplementedError): + lpass.lpass_field('my_name', 'url') + + def test_lpass_field_notes_raises(self): + with self.assertRaises(NotImplementedError): + lpass.lpass_field('my_name', 'notes') @patch('db_facts.lpass.check_output') def test_lpass_field_username(self, mock_check_output): mock_check_output.return_value = "fakeuser\n".encode("utf-8") out = lpass.lpass_field('my_name', 'username') - mock_check_output.\ - assert_called_with(['lpass', 'show', '--username', 'my_name']) + mock_check_output.assert_called_with( + ['op', 'item', 'get', 'my_name', '--field', 'label=username']) assert out == "fakeuser" @patch('db_facts.lpass.check_output') def test_lpass_field_password(self, mock_check_output): mock_check_output.return_value = "fakepassword\n".encode("utf-8") out = lpass.lpass_field('my_name', 'password') - mock_check_output.\ - assert_called_with(['lpass', 'show', '--password', 'my_name']) + mock_check_output.assert_called_with( + ['op', 'item', 'get', 'my_name', '--field', 'label=password']) assert out == "fakepassword" - @patch('db_facts.lpass.check_output') - def test_lpass_field_url(self, mock_check_output): - mock_check_output.return_value = "fakeurl\n".encode("utf-8") - out = lpass.lpass_field('my_name', 'url') - mock_check_output.\ - assert_called_with(['lpass', 'show', '--url', 'my_name']) - assert out == "fakeurl" - @patch('db_facts.lpass.check_output') def test_lpass_field_field1(self, mock_check_output): mock_check_output.return_value = "fakefield1\n".encode("utf-8") out = lpass.lpass_field('my_name', 'field1') - mock_check_output.\ - assert_called_with(['lpass', 'show', '--field=field1', 'my_name']) + mock_check_output.assert_called_with( + ['op', 'item', 'get', 'my_name', '--field', 'label=field1']) assert out == "fakefield1" @patch('db_facts.lpass.check_output') def test_db_info_from_lpass(self, mock_check_output): def fake_check_output(args): - assert args[0] == 'lpass' - assert args[1] == 'show' + assert args[0] == 'op' + assert args[1] == 'item' + assert args[2] == 'get' assert args[3] == 'my_lpass_name' + assert args[4] == '--field' ret = { - "--username": 'fakeuser', - "--password": 'fakepassword', - "--field=Hostname": 'fakehost', - "--field=Port": '123', - "--field=Type": 'faketype', - "--field=Database": 'fakedatabase', + "label=username": 'fakeuser', + "label=password": 'fakepassword', + "label=Hostname": 'fakehost', + "label=Port": '123', + "label=Type": 'faketype', + "label=Database": 'fakedatabase', } - return (ret[args[2]] + "\n").encode('utf-8') + return (ret[args[5]] + "\n").encode('utf-8') mock_check_output.side_effect = fake_check_output db_info = lpass.db_info_from_lpass('my_lpass_name') expected_db_info = {