Skip to content

Commit 8bc4f7a

Browse files
committed
Convert authenticators to pydantic models
1 parent 87c3432 commit 8bc4f7a

File tree

6 files changed

+133
-188
lines changed

6 files changed

+133
-188
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tiled catalog upgrade-database [postgresql://.. | sqlite:///...]
2727
- Refactored internal Zarr version detection
2828
- For compatibility with older clients, do not require metadata updates to include
2929
an `access_blob` in the body of the request.
30+
- Convert authenticators into pydantic models
3031

3132
### Fixed
3233

tiled/_tests/test_access_control.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import json
2+
import secrets
3+
from typing import Any, Optional
24

35
import numpy
46
import pytest
57
from starlette.status import HTTP_403_FORBIDDEN
8+
from typing_extensions import TypedDict
69

7-
from tiled.authenticators import DictionaryAuthenticator
8-
from tiled.server.protocols import UserSessionState
10+
from tiled.server.protocols import InternalAuthenticator, UserSessionState
911

1012
from ..access_policies import NO_ACCESS
1113
from ..adapters.array import ArrayAdapter
@@ -531,22 +533,23 @@ def test_service_principal_access(tmpdir, sqlite_or_postgres_uri):
531533
assert list(sp_client) == ["x"]
532534

533535

534-
class CustomAttributesAuthenticator(DictionaryAuthenticator):
536+
UserAttributes = TypedDict(
537+
"UserAttributes", {"password": str, "attributes": dict[str, Any]}, total=False
538+
)
539+
540+
541+
class CustomAttributesAuthenticator(InternalAuthenticator):
535542
"""An example authenticator that enriches the stored user information."""
536543

537-
def __init__(self, users: dict, confirmation_message: str = ""):
538-
self._users = users
539-
super().__init__(
540-
{username: user["password"] for username, user in users.items()},
541-
confirmation_message,
542-
)
543-
544-
async def authenticate(self, username, password):
545-
state = await super().authenticate(username, password)
546-
if isinstance(state, UserSessionState):
547-
# enrich the auth state
548-
state.state["attributes"] = self._users[username].get("attributes", {})
549-
return state
544+
users: dict[str, UserAttributes] = {}
545+
546+
async def authenticate(self, username, password) -> Optional[UserSessionState]:
547+
if (attrs := self.users.get(username)) and (pw := attrs.get("password")):
548+
if secrets.compare_digest(pw, password):
549+
state = UserSessionState(
550+
username, {"attributes": attrs.get("attributes", {})}
551+
)
552+
return state
550553

551554

552555
class CustomAttributesAccessPolicy:

tiled/_tests/test_authenticators.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@
2525
])
2626
# fmt: on
2727
@pytest.mark.parametrize("use_tls,use_ssl", [(False, False)])
28+
@pytest.mark.skipif(not TILED_TEST_LDAP, reason="Requires an LDAP container and TILED_TEST_LDAP to be set")
2829
def test_LDAPAuthenticator_01(use_tls, use_ssl, ldap_server_address, ldap_server_port):
2930
"""
3031
Basic test for ``LDAPAuthenticator``.
3132
3233
TODO: The test could be extended with enabled TLS or SSL, but it requires configuration
3334
of the LDAP server.
3435
"""
35-
if not TILED_TEST_LDAP:
36-
pytest.skip("Run an LDAP container and set TILED_TEST_LDAP to run")
36+
3737
authenticator = LDAPAuthenticator(
38-
ldap_server_address,
39-
ldap_server_port,
38+
server_address=ldap_server_address,
4039
bind_dn_template="cn={username},ou=users,dc=example,dc=org",
41-
use_tls=use_tls,
4240
use_ssl=use_ssl,
41+
use_tls=use_tls,
42+
server_port=ldap_server_port
4343
)
4444

4545
async def testing():
@@ -49,3 +49,19 @@ async def testing():
4949
assert (await authenticator.authenticate("user02", "password2a")) is None
5050

5151
asyncio.run(testing())
52+
53+
54+
def test_ldap_port_validation():
55+
# given port can be none but will be replaced with a default
56+
auth = LDAPAuthenticator(server_address="http://ldap.example.com", server_port=None)
57+
assert auth.server_port is not None
58+
59+
60+
def test_auth_server_list_wrapping():
61+
auth = LDAPAuthenticator(server_address="http://ldap.example.com", server_port=None)
62+
assert auth.server_address_list == ["http://ldap.example.com"]
63+
64+
65+
def test_list_of_addresses_not_nested_into_extra_list():
66+
auth = LDAPAuthenticator(server_address=["http://ldap.example.com"])
67+
assert auth.server_address_list == ["http://ldap.example.com"]

0 commit comments

Comments
 (0)