fix authentication errors #238

Merged
cmo merged 2 commits from fix-authentication into master 2026-06-26 09:34:24 +00:00
4 changed files with 154 additions and 30 deletions

View file

@ -1,3 +1,10 @@
# 6.2.2 (2026-06-26)
# Bugfixes
- Fix a bug in authentication when non-config tokens are used.
# 6.2.1 (2026-06-26)
# Bugfixes

View file

@ -1 +1 @@
__version__ = '6.2.1'
__version__ = '6.2.2'

View file

@ -0,0 +1,81 @@
from __future__ import annotations
import logging
from itertools import count
from typing import TYPE_CHECKING
from fastapi import (
APIRouter,
Depends,
FastAPI,
HTTPException,
)
from fastapi_pagination import (
Page,
add_pagination,
paginate,
)
from dump_things_service import (
HTTP_401_UNAUTHORIZED,
HTTP_404_NOT_FOUND,
HTTP_422_UNPROCESSABLE_CONTENT, abstract_config,
)
from dump_things_service.abstract_config import (
check_collection,
read_config,
)
from dump_things_service.api_key import api_key_header_scheme
from dump_things_service.auth import AuthenticationInfo
from dump_things_service.backends.schema_type_layer import _SchemaTypeLayer
from dump_things_service.exceptions import CurieResolutionError
from dump_things_service.instance_state import get_instance_state
from dump_things_service.lazy_list import ModifierList
from dump_things_service.utils import (
authenticate_token,
check_bounds,
cleaned_json,
wrap_http_exception,
)
if TYPE_CHECKING:
from pydantic import BaseModel
from dump_things_service.backends import StorageBackend
from dump_things_service.lazy_list import LazyList
from dump_things_service.store.model_store import _ModelStore
def get_store_and_backend(
collection: str,
plain_token: str | None,
) -> tuple[_ModelStore, StorageBackend, AuthenticationInfo]:
# A token is required
if plain_token is None:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail='token required',
)
instance_state = get_instance_state()
abstract_config = read_config(instance_state.store_path)
# Check that the collection exists
check_collection(abstract_config=abstract_config, collection=collection)
# Get token permissions
auth_info = authenticate_token(instance_state, collection, plain_token)
permissions = auth_info.token_permission
if permissions.curated_write is False:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail=f'no write access to curated area of collection `{collection}`',
)
# Get the curated model store
model_store = instance_state.curated_stores[collection]
backend = model_store.backend
if isinstance(backend, _SchemaTypeLayer):
return model_store, backend.backend, auth_info
return model_store, backend, auth_info

View file

@ -14,7 +14,6 @@ from contextlib import contextmanager
from functools import reduce
from typing import (
TYPE_CHECKING,
Callable,
)
import fsspec
@ -30,14 +29,14 @@ from dump_things_service import (
)
from dump_things_service.abstract_config import (
Configuration,
RecordDirBackendConfig,
TokenModes,
TokenPermission,
mode_mapping,
check_collection,
get_collection_config_by_name,
get_default_token_config,
get_token_config_for_representation_and_collection,
get_mapping_function_by_name,
get_token_config_for_representation_and_collection,
)
from dump_things_service.auth import (
AuthenticationError,
@ -206,20 +205,63 @@ def authenticate_token(
return auth_info
def get_default_token_auth_info(
abstract_config: Configuration,
collection_name: str,
token_name: str,
) -> AuthenticationInfo:
token_config = abstract_config.tokens[token_name]
collection_info = token_config.collections.get(collection_name)
if collection_info is None:
detail = f"Default token not valid for collection '{collection_name}'"
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail=detail,
)
return AuthenticationInfo(
token_permission=mode_mapping[TokenModes(collection_info.mode)],
user_id = token_config.user_id,
incoming_label = collection_info.incoming_label,
)
def get_token_store(
abstract_config: Configuration,
instance_state: InstanceState,
collection_name: str,
token_representation: str,
token_representation: str | None,
*,
is_token_name: bool = False,
) -> tuple[_ModelStore, TokenPermission, str] | tuple[None, None, None, None]:
# Try to authenticate the token with the authentication providers that
# are associated with the collection.
# If a token representation is provided, try to authenticate the token
# with the authentication providers that are associated with the collection.
if not is_token_name:
if token_representation is None:
msg = 'get_token_store: token_representation is None and is_token_name is False. This is a calling error!'
logger.error(msg)
raise ValueError(msg)
auth_info = authenticate_token(
instance_state,
collection_name,
token_representation,
)
else:
auth_info = get_default_token_auth_info(
abstract_config=abstract_config,
collection_name=collection_name,
token_name=token_representation,
)
token_representation = None
if not auth_info:
detail = f"invalid token for collection '{collection_name}'"
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail=detail,
)
permissions = auth_info.token_permission
# If the token has no incoming-read or incoming-write permissions, we do not
@ -429,28 +471,22 @@ async def process_token(
) -> tuple[TokenPermission, _ModelStore]:
if api_key is None:
token_config = get_default_token_config(abstract_config, collection)
else:
token_elements = get_token_config_for_representation_and_collection(
abstract_config,
collection_name=collection,
token_representation=api_key,
)
token_config = token_elements[1] if token_elements else None
if not token_config:
detail = f"invalid token for collection '{collection}'"
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail=detail,
)
collection_config = get_collection_config_by_name(abstract_config, collection)
token_store, token_permissions, user_id = get_token_store(
abstract_config,
instance_state,
collection,
token_config.representation,
token_representation=collection_config.default_token,
is_token_name=True,
)
else:
token_store, token_permissions, user_id = get_token_store(
abstract_config,
instance_state,
collection,
api_key,
)
final_permissions = join_default_token_permissions(
abstract_config, instance_state, token_permissions, collection
)