Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8adaa79
Updating authenticators
davidpcls Jul 6, 2026
a3bbe20
Migrating changes from 0.2.12 tiled in
davidpcls Jul 6, 2026
e0fa539
Adding new port tests
davidpcls Jul 6, 2026
f4b892c
Removing lazy import
davidpcls Jul 7, 2026
faed6f7
Removing my dumb changes
davidpcls Jul 8, 2026
a79e5eb
Cleaning up the shutdown changes
davidpcls Jul 9, 2026
c0430e4
Adding auto-upgrade ability
davidpcls Jul 9, 2026
22cc93c
Fixing typo
davidpcls Jul 9, 2026
b69bd45
Code review adjustments
davidpcls Jul 9, 2026
ab388c5
Removing timezone changes to reduce scope
davidpcls Jul 9, 2026
3259083
Removing unnecessary formatting changes
davidpcls Jul 9, 2026
b459854
Some more cleanup
davidpcls Jul 10, 2026
e970e54
Fixing linting issue reported by isort
davidpcls Jul 10, 2026
2dce363
Cleaning up pre-commit check
davidpcls Jul 10, 2026
f5c0326
Fixed up unit tests
davidpcls Jul 10, 2026
fbaaff5
Fixing linting issue
davidpcls Jul 10, 2026
e6d1c96
Making tests more robust
davidpcls Jul 10, 2026
d4c0e6a
Pre-commit checks
davidpcls Jul 10, 2026
36fcb4a
Fixing up test
davidpcls Jul 10, 2026
392f1e8
Refactoring unit tests
davidpcls Jul 13, 2026
72b9aaa
Fixed up unit tests after refactoring
davidpcls Jul 13, 2026
32f04cd
Fixing pre-commit errors
davidpcls Jul 13, 2026
43b138b
Cleanup of the get_current_principal func
davidpcls Jul 14, 2026
fede804
Updating unit test
davidpcls Jul 16, 2026
b4fc44f
Working version with MS graph API
davidpcls Jul 16, 2026
852b082
Cleanup from pre-commit
davidpcls Jul 16, 2026
cfb1e39
Fixes and cleanup to the authenticators
davidpcls Jul 20, 2026
4d5081f
Pre-commit fixes
davidpcls Jul 20, 2026
6c3f0fe
Cleaning up issues with function renaming in tests
davidpcls Jul 20, 2026
93fc98f
Cleanup test errors
davidpcls Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 43 additions & 44 deletions bluesky_httpserver/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@
from fastapi.middleware.cors import CORSMiddleware
from fastapi.openapi.utils import get_openapi

from .authentication import ExternalAuthenticator, InternalAuthenticator
from .console_output import CollectPublishedConsoleOutput, ConsoleOutputStream, SystemInfoStream
from .authenticators import ProxiedOIDCAuthenticator
from .console_output import (
CollectPublishedConsoleOutput,
ConsoleOutputStream,
SystemInfoStream,
)
from .core import PatchedStreamingResponse
from .database.core import purge_expired
from .protocols import ExternalAuthenticator, InternalAuthenticator
from .resources import SERVER_RESOURCES as SR
from .routers import core_api
from .settings import get_settings
Expand Down Expand Up @@ -158,14 +163,9 @@ def build_app(authentication=None, api_access=None, resource_access=None, server
logger.info("All custom routers are included successfully.")

from .authentication import (
add_external_routes,
add_internal_routes,
base_authentication_router,
build_auth_code_route,
build_authorize_route,
build_device_code_authorize_route,
build_device_code_form_route,
build_device_code_submit_route,
build_device_code_token_route,
build_handle_credentials_route,
oauth2_scheme,
)

Expand All @@ -185,38 +185,11 @@ def build_app(authentication=None, api_access=None, resource_access=None, server
provider = spec["provider"]
authenticator = spec["authenticator"]
if isinstance(authenticator, InternalAuthenticator):
authentication_router.post(f"/provider/{provider}/token")(
build_handle_credentials_route(authenticator, provider)
)
add_internal_routes(authentication_router, provider, authenticator)
elif isinstance(authenticator, ExternalAuthenticator):
# Standard OAuth callback route (authorization code flow)
authentication_router.get(f"/provider/{provider}/code")(
build_auth_code_route(authenticator, provider)
)
authentication_router.post(f"/provider/{provider}/code")(
build_auth_code_route(authenticator, provider)
)
# Device code flow routes for CLI/headless clients
# GET /authorize - redirects browser to OIDC provider
authentication_router.get(f"/provider/{provider}/authorize")(
build_authorize_route(authenticator, provider)
)
# POST /authorize - initiates device code flow (returns device_code, user_code, etc.)
authentication_router.post(f"/provider/{provider}/authorize")(
build_device_code_authorize_route(authenticator, provider)
)
# GET /device_code - shows user code entry form
authentication_router.get(f"/provider/{provider}/device_code")(
build_device_code_form_route(authenticator, provider)
)
# POST /device_code - handles user code submission after browser auth
authentication_router.post(f"/provider/{provider}/device_code")(
build_device_code_submit_route(authenticator, provider)
)
# POST /token - CLI client polls this for tokens
authentication_router.post(f"/provider/{provider}/token")(
build_device_code_token_route(authenticator, provider)
)
add_external_routes(authentication_router, provider, authenticator)
if isinstance(authenticator, ProxiedOIDCAuthenticator):
app.state.provider = provider
else:
raise ValueError(f"unknown authenticator type {type(authenticator)}")
for custom_router in getattr(authenticator, "include_routers", []):
Expand Down Expand Up @@ -262,9 +235,11 @@ async def startup_event():
from .database import orm
from .database.core import ( # make_admin_by_identity,
REQUIRED_REVISION,
DatabaseUpgradeNeeded,
UninitializedDatabase,
check_database,
initialize_database,
upgrade,
)

connect_args = {}
Expand All @@ -282,6 +257,10 @@ async def startup_event():
)
initialize_database(engine)
logger.info("Database initialized.")
except DatabaseUpgradeNeeded:
logger.info(f"Database at {redacted_url} is out of date. Upgrading to {REQUIRED_REVISION}...")
upgrade(engine, REQUIRED_REVISION)
logger.info("Database upgraded.")
Comment thread
davidpcls marked this conversation as resolved.
else:
logger.info(f"Connected to existing database at {redacted_url}.")
# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Expand Down Expand Up @@ -416,10 +395,30 @@ async def purge_expired_sessions_and_api_keys():

@app.on_event("shutdown")
async def shutdown_event():
await SR.RM.close()
await SR.console_output_loader.stop()
await SR.console_output_stream.stop()
await SR.system_info_stream.stop()
"""Safely shutdown and perform the cleanup robustly

This change ensures that the application shuts down and cleans up resources even if there is
a problem, without silencing the errors.
"""
for task in getattr(app.state, "tasks", []):
task.cancel()
for closer_name in (
"console_output_loader",
"console_output_stream",
"system_info_stream",
):
closer = getattr(SR, closer_name, None)
if closer is not None:
try:
await closer.stop()
except Exception:
logger.exception("Error stopping %s", closer_name)
rm = getattr(SR, "RM", None)
if rm is not None:
try:
await rm.close()
except Exception:
logger.exception("Error closing REManagerAPI connection")

@lru_cache(1)
def override_get_authenticators():
Expand Down
Loading
Loading