Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔑 Add authenticated property to context #917

Merged
merged 7 commits into from
Mar 7, 2025

Conversation

Kezzsim
Copy link
Contributor

@Kezzsim Kezzsim commented Mar 7, 2025

Resolves #818

Checklist

  • Have the authenticated property return True or False depending on the state of the client
  • Have the authenticated property show up in the tab context menu of ipython or in the output of dir(c.context)

Optional

  • Write a test for authenticated property
  • Type assertion

@danielballan
Copy link
Member

danielballan commented Mar 7, 2025

If we set this attribute inside __repr__ we'll have an odd behavior wherein it is only set (and updated) when __repr__ is called. Take a look at this:

In [1]: from tiled.client import from_profile

In [2]: client = from_profile('demo')

In [3]: 'authenticated' in dir(client.context)
Out[3]: False

In [4]: client.context  # Displaying the object calls `__repr__()` internally.
Out[4]: <Context (unauthenticated)>

In [5]: 'authenticated' in dir(client.context)
Out[5]: True

In #818 I suggested "exposing it as a property", i.e.

class Context:

    ...


    @property
    def authenticated(self):
        return ...

Thus, authenticated will always be defined, and the current value will be computed when the user checks it.

@danielballan
Copy link
Member

In this test, we check whether the repr shows the Context being authenticated or not. This would be a good place to test the new authenticated property has the expected value.

def test_password_auth(enter_username_password, config):
"""
A password that is wrong, empty, or belonging to a different user fails.
"""
with Context.from_app(build_app_from_config(config)) as context:
# Log in as Alice.
with enter_username_password("alice", "secret1"):
from_context(context)
# Reuse token from cache.
client = from_context(context)
assert "authenticated as 'alice'" in repr(client.context)
client.logout()
assert "unauthenticated" in repr(client.context)
# Log in as Bob.
with enter_username_password("bob", "secret2"):
client = from_context(context)
assert "authenticated as 'bob'" in repr(client.context)
client.logout()
# Bob's password should not work for Alice.
with pytest.raises(PasswordRejected):
with enter_username_password("alice", "secret2"):
from_context(context)
# Empty password should not work.
with pytest.raises(PasswordRejected):
with enter_username_password("alice", ""):
from_context(context)

And here for the API key case:

def test_api_key_activity(enter_username_password, config):
"""
Create and use an API. Verify that latest_activity updates.
"""
with Context.from_app(build_app_from_config(config)) as context:
# Log in as user.
with enter_username_password("alice", "secret1"):
context.authenticate()
# Make and use an API key. Check that latest_activity is not set.
key_info = context.create_api_key()
context.logout()
assert key_info["latest_activity"] is None # never used
context.api_key = key_info["secret"]
assert "authenticated as 'alice'" in repr(context)
assert "with API key" in repr(context)
assert key_info["secret"][:8] in repr(context)
assert key_info["secret"][8:] not in repr(context)

@Kezzsim Kezzsim requested a review from nmaytan March 7, 2025 17:26
Copy link
Contributor

@nmaytan nmaytan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested and looks good!

@danielballan danielballan merged commit 4bc8b6b into bluesky:main Mar 7, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add an authenticated property
3 participants