Skip to content

Commit 7143842

Browse files
authored
Updating authenticators (#9)
* Updating authenticators * Migrating changes from 0.2.12 tiled in * Adding new port tests * Removing lazy import * Removing my dumb changes * Cleaning up the shutdown changes * Adding auto-upgrade ability * Fixing typo * Code review adjustments * Removing timezone changes to reduce scope * Removing unnecessary formatting changes This just makes the diff easier to understand * Some more cleanup * Fixing linting issue reported by isort * Cleaning up pre-commit check * Fixed up unit tests * Fixing linting issue * Making tests more robust * Pre-commit checks * Fixing up test * Refactoring unit tests * Fixed up unit tests after refactoring Moved things around to look more like tiled * Fixing pre-commit errors * Cleanup of the get_current_principal func It was just too large, so moving it towards tiled style as much as possible. Still not perfect but at least better. Tests need updating. * Updating unit test * Working version with MS graph API * Cleanup from pre-commit * Fixes and cleanup to the authenticators * Pre-commit fixes * Cleaning up issues with function renaming in tests * Cleanup test errors
1 parent 2a12569 commit 7143842

19 files changed

Lines changed: 1707 additions & 404 deletions

bluesky_httpserver/app.py

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
from fastapi.middleware.cors import CORSMiddleware
1616
from fastapi.openapi.utils import get_openapi
1717

18-
from .authentication import ExternalAuthenticator, InternalAuthenticator
19-
from .console_output import CollectPublishedConsoleOutput, ConsoleOutputStream, SystemInfoStream
18+
from .authenticators import ProxiedOIDCAuthenticator
19+
from .console_output import (
20+
CollectPublishedConsoleOutput,
21+
ConsoleOutputStream,
22+
SystemInfoStream,
23+
)
2024
from .core import PatchedStreamingResponse
2125
from .database.core import purge_expired
26+
from .protocols import ExternalAuthenticator, InternalAuthenticator
2227
from .resources import SERVER_RESOURCES as SR
2328
from .routers import core_api
2429
from .settings import get_settings
@@ -158,14 +163,9 @@ def build_app(authentication=None, api_access=None, resource_access=None, server
158163
logger.info("All custom routers are included successfully.")
159164

160165
from .authentication import (
166+
add_external_routes,
167+
add_internal_routes,
161168
base_authentication_router,
162-
build_auth_code_route,
163-
build_authorize_route,
164-
build_device_code_authorize_route,
165-
build_device_code_form_route,
166-
build_device_code_submit_route,
167-
build_device_code_token_route,
168-
build_handle_credentials_route,
169169
oauth2_scheme,
170170
)
171171

@@ -185,38 +185,11 @@ def build_app(authentication=None, api_access=None, resource_access=None, server
185185
provider = spec["provider"]
186186
authenticator = spec["authenticator"]
187187
if isinstance(authenticator, InternalAuthenticator):
188-
authentication_router.post(f"/provider/{provider}/token")(
189-
build_handle_credentials_route(authenticator, provider)
190-
)
188+
add_internal_routes(authentication_router, provider, authenticator)
191189
elif isinstance(authenticator, ExternalAuthenticator):
192-
# Standard OAuth callback route (authorization code flow)
193-
authentication_router.get(f"/provider/{provider}/code")(
194-
build_auth_code_route(authenticator, provider)
195-
)
196-
authentication_router.post(f"/provider/{provider}/code")(
197-
build_auth_code_route(authenticator, provider)
198-
)
199-
# Device code flow routes for CLI/headless clients
200-
# GET /authorize - redirects browser to OIDC provider
201-
authentication_router.get(f"/provider/{provider}/authorize")(
202-
build_authorize_route(authenticator, provider)
203-
)
204-
# POST /authorize - initiates device code flow (returns device_code, user_code, etc.)
205-
authentication_router.post(f"/provider/{provider}/authorize")(
206-
build_device_code_authorize_route(authenticator, provider)
207-
)
208-
# GET /device_code - shows user code entry form
209-
authentication_router.get(f"/provider/{provider}/device_code")(
210-
build_device_code_form_route(authenticator, provider)
211-
)
212-
# POST /device_code - handles user code submission after browser auth
213-
authentication_router.post(f"/provider/{provider}/device_code")(
214-
build_device_code_submit_route(authenticator, provider)
215-
)
216-
# POST /token - CLI client polls this for tokens
217-
authentication_router.post(f"/provider/{provider}/token")(
218-
build_device_code_token_route(authenticator, provider)
219-
)
190+
add_external_routes(authentication_router, provider, authenticator)
191+
if isinstance(authenticator, ProxiedOIDCAuthenticator):
192+
app.state.provider = provider
220193
else:
221194
raise ValueError(f"unknown authenticator type {type(authenticator)}")
222195
for custom_router in getattr(authenticator, "include_routers", []):
@@ -262,9 +235,11 @@ async def startup_event():
262235
from .database import orm
263236
from .database.core import ( # make_admin_by_identity,
264237
REQUIRED_REVISION,
238+
DatabaseUpgradeNeeded,
265239
UninitializedDatabase,
266240
check_database,
267241
initialize_database,
242+
upgrade,
268243
)
269244

270245
connect_args = {}
@@ -282,6 +257,10 @@ async def startup_event():
282257
)
283258
initialize_database(engine)
284259
logger.info("Database initialized.")
260+
except DatabaseUpgradeNeeded:
261+
logger.info(f"Database at {redacted_url} is out of date. Upgrading to {REQUIRED_REVISION}...")
262+
upgrade(engine, REQUIRED_REVISION)
263+
logger.info("Database upgraded.")
285264
else:
286265
logger.info(f"Connected to existing database at {redacted_url}.")
287266
# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
@@ -416,10 +395,30 @@ async def purge_expired_sessions_and_api_keys():
416395

417396
@app.on_event("shutdown")
418397
async def shutdown_event():
419-
await SR.RM.close()
420-
await SR.console_output_loader.stop()
421-
await SR.console_output_stream.stop()
422-
await SR.system_info_stream.stop()
398+
"""Safely shutdown and perform the cleanup robustly
399+
400+
This change ensures that the application shuts down and cleans up resources even if there is
401+
a problem, without silencing the errors.
402+
"""
403+
for task in getattr(app.state, "tasks", []):
404+
task.cancel()
405+
for closer_name in (
406+
"console_output_loader",
407+
"console_output_stream",
408+
"system_info_stream",
409+
):
410+
closer = getattr(SR, closer_name, None)
411+
if closer is not None:
412+
try:
413+
await closer.stop()
414+
except Exception:
415+
logger.exception("Error stopping %s", closer_name)
416+
rm = getattr(SR, "RM", None)
417+
if rm is not None:
418+
try:
419+
await rm.close()
420+
except Exception:
421+
logger.exception("Error closing REManagerAPI connection")
423422

424423
@lru_cache(1)
425424
def override_get_authenticators():

0 commit comments

Comments
 (0)