Skip to content

Commit

Permalink
auth: handle keyring unlocking errors (#75)
Browse files Browse the repository at this point in the history
Obtaining the password from the keyring can fail if the keyring is
not properly unlocked. In this case, handle the keyring exception and
raise an appropriate Craft-store error.

Signed-off-by: Claudio Matsuoka <claudio.matsuoka@canonical.com>
  • Loading branch information
cmatsuoka committed Feb 23, 2023
1 parent bacd67f commit 74a2ed4
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 2 deletions.
10 changes: 8 additions & 2 deletions craft_store/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,15 @@ def ensure_no_credentials(self) -> None:
"""Check that no credentials exist.
:raises errors.CredentialsAvailable: if credentials have already been set.
:raises errors.KeyringUnlockError: if the keyring cannot be unlocked.
"""
if self._keyring.get_password(self.application_name, self.host) is not None:
raise errors.CredentialsAlreadyAvailable(self.application_name, self.host)
try:
if self._keyring.get_password(self.application_name, self.host) is not None:
raise errors.CredentialsAlreadyAvailable(
self.application_name, self.host
)
except keyring.errors.KeyringLocked:
raise errors.KeyringUnlockError()

def set_credentials(self, credentials: str, force: bool = False) -> None:
"""Store credentials in the keyring.
Expand Down
10 changes: 10 additions & 0 deletions craft_store/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@ def __init__(self) -> None:
super().__init__("No keyring found to store or retrieve credentials from.")


class KeyringUnlockError(CraftStoreError):
"""Error raised when keyring unlocking fails."""

def __init__(self) -> None:
super().__init__(
"Failed to unlock the keyring.",
resolution="Make sure the password is correct and the keyring is available.",
)


class CandidTokenTimeoutError(CraftStoreError):
"""Error raised when timeout is reached trying to discharge a macaroon."""

Expand Down
11 changes: 11 additions & 0 deletions tests/unit/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,17 @@ def test_environment_set(monkeypatch, fake_keyring, keyring_set_keyring_mock):
]


def test_ensure_no_credentials_unlock_error(fake_keyring, mocker):
mocker.patch.object(
fake_keyring, "get_password", side_effect=errors.KeyringUnlockError
)

auth = Auth("fakeclient", "fakestore.com")

with pytest.raises(errors.KeyringUnlockError):
auth.ensure_no_credentials()


@pytest.mark.disable_fake_keyring
def test_ephemeral_set_memory_keyring():
auth = Auth("fakeclient", "fakestore.com", ephemeral=True)
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ def _fake_error_response(status_code, reason, json=None):
"args": [],
"expected_message": "No keyring found to store or retrieve credentials from.",
},
{
"exception_class": errors.KeyringUnlockError,
"args": [],
"expected_message": "Failed to unlock the keyring.",
},
{
"exception_class": errors.CredentialsAlreadyAvailable,
"args": ["mycraft", "my.host.com"],
Expand Down

0 comments on commit 74a2ed4

Please sign in to comment.