Skip to content

Commit 3a4ad0b

Browse files
committed
web: move remote reload token read logic to core/config.py
1 parent d9f30c2 commit 3a4ad0b

File tree

3 files changed

+72
-62
lines changed

3 files changed

+72
-62
lines changed

web/api/v1/admin.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77

88
router = APIRouter(prefix="/admin", tags=["admin"])
9-
security = HTTPBearer()
9+
security = HTTPBearer(auto_error=False)
1010

1111

12-
async def verify_admin_token(
12+
async def verify_remote_reload_token(
1313
credentials: HTTPAuthorizationCredentials = Depends(security),
1414
admin_service: AdminService = Depends(get_admin_service)
1515
) -> None:
1616
"""
17-
Verify the bearer token for admin authentication.
17+
Verify the bearer token for remote reload authentication.
1818
1919
Args:
2020
credentials: HTTP authorization credentials from request header
@@ -24,9 +24,15 @@ async def verify_admin_token(
2424
401: Invalid or missing token
2525
500: Server configuration error (token not configured)
2626
"""
27+
if credentials is None or credentials.scheme.lower() != "bearer":
28+
raise HTTPException(
29+
status_code=status.HTTP_401_UNAUTHORIZED,
30+
detail="Missing or invalid authentication token"
31+
)
32+
2733
token = credentials.credentials
2834
try:
29-
if not await admin_service.verify_token(token):
35+
if not await admin_service.verify_remote_reload_token(token):
3036
raise HTTPException(
3137
status_code=status.HTTP_401_UNAUTHORIZED,
3238
detail="Invalid authentication token"
@@ -52,7 +58,7 @@ async def verify_admin_token(
5258
}
5359
)
5460
async def refresh_remotes(
55-
_: None = Depends(verify_admin_token),
61+
_: None = Depends(verify_remote_reload_token),
5662
admin_service: AdminService = Depends(get_admin_service)
5763
):
5864
"""

web/core/config.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
Application configuration and settings.
33
"""
44
import os
5+
import logging
56
from pathlib import Path
67
from functools import lru_cache
8+
from typing import Optional
9+
10+
11+
logger = logging.getLogger(__name__)
712

813

914
class Settings:
@@ -62,21 +67,40 @@ def remotes_json_path(self) -> str:
6267
"""Path to remotes.json configuration."""
6368
return os.path.join(self.base_dir, 'configs', 'remotes.json')
6469

65-
@property
66-
def admin_token_file_path(self) -> str:
67-
"""Path to admin token secret file."""
68-
return os.path.join(self.base_dir, 'secrets', 'reload_token')
69-
7070
@property
7171
def enable_inbuilt_builder(self) -> bool:
7272
"""Whether to enable the inbuilt builder."""
7373
return os.getenv('CBS_ENABLE_INBUILT_BUILDER', '1') == '1'
7474

7575
@property
76-
def admin_token_env(self) -> str:
77-
"""Token required to reload remotes.json via API."""
78-
env = os.getenv('CBS_REMOTES_RELOAD_TOKEN', '')
79-
return env if env != '' else None
76+
def remote_reload_token(self) -> Optional[str]:
77+
"""
78+
Get remote reload token from file or environment variable.
79+
80+
Tries to read token from file first, falls back to environment variable.
81+
82+
Returns:
83+
The authorization token if found, None otherwise
84+
"""
85+
token_file_path = os.path.join(self.base_dir, 'secrets', 'reload_token')
86+
87+
try:
88+
# Try to read the secret token from the file
89+
with open(token_file_path, 'r') as file:
90+
token = file.read().strip()
91+
return token
92+
except (FileNotFoundError, PermissionError):
93+
# If the file does not exist or no permission, check environment
94+
env_token = os.getenv('CBS_REMOTES_RELOAD_TOKEN', '')
95+
return env_token if env_token != '' else None
96+
except Exception as e:
97+
logger.error(
98+
f"Unexpected error reading token file at {token_file_path}: {e}. "
99+
"Checking environment for token."
100+
)
101+
# For any other error, fall back to environment variable
102+
env_token = os.getenv('CBS_REMOTES_RELOAD_TOKEN', None)
103+
return env_token if env_token != '' else None
80104

81105

82106
@lru_cache()

web/services/admin.py

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,78 +2,43 @@
22
Admin service for handling administrative operations.
33
"""
44
import logging
5-
from typing import Optional, List
5+
from typing import List
66

7-
from fastapi import Request
7+
from fastapi import Depends, Request
8+
from web.core.config import get_settings, Settings
89

9-
from core.config import get_settings
1010

1111
logger = logging.getLogger(__name__)
1212

1313

1414
class AdminService:
1515
"""Service for managing administrative operations."""
1616

17-
def __init__(self, versions_fetcher=None):
17+
def __init__(self, remote_reload_token: str, versions_fetcher=None):
1818
"""
1919
Initialize the admin service.
2020
2121
Args:
22+
remote_reload_token: Remote reload authentication token
2223
versions_fetcher: VersionsFetcher instance for managing remotes
2324
"""
25+
self.remote_reload_token = remote_reload_token
2426
self.versions_fetcher = versions_fetcher
25-
self.settings = get_settings()
2627

27-
def get_auth_token(self) -> Optional[str]:
28+
async def verify_remote_reload_token(self, token: str) -> bool:
2829
"""
29-
Retrieve the authorization token from file or environment.
30-
31-
Returns:
32-
The authorization token if found, None otherwise
33-
"""
34-
try:
35-
# Try to read the secret token from the file
36-
token_file_path = self.settings.admin_token_file_path
37-
with open(token_file_path, 'r') as file:
38-
token = file.read().strip()
39-
return token
40-
except (FileNotFoundError, PermissionError) as e:
41-
logger.error(
42-
f"Couldn't open token file at "
43-
f"{self.settings.admin_token_file_path}: {e}. "
44-
"Checking environment for token."
45-
)
46-
# If the file does not exist or no permission, check environment
47-
return self.settings.admin_token_env
48-
except Exception as e:
49-
logger.error(
50-
f"Unexpected error reading token file at "
51-
f"{self.settings.admin_token_file_path}: {e}. "
52-
"Checking environment for token."
53-
)
54-
# For any other error, fall back to environment variable
55-
return self.settings.admin_token_env
56-
57-
async def verify_token(self, token: str) -> bool:
58-
"""
59-
Verify that the provided token matches the expected admin token.
30+
Verify that the provided token matches the expected remote reload token.
6031
6132
Args:
6233
token: The token to verify
6334
6435
Returns:
6536
True if token is valid, False otherwise
66-
67-
Raises:
68-
RuntimeError: If admin token is not configured on server
6937
"""
70-
expected_token = self.get_auth_token()
71-
72-
if expected_token is None:
73-
logger.error("No admin token configured")
74-
raise RuntimeError("Admin token not configured on server")
38+
if not token:
39+
return False
7540

76-
return token == expected_token
41+
return token == self.remote_reload_token
7742

7843
async def refresh_remotes(self) -> List[str]:
7944
"""
@@ -102,14 +67,29 @@ async def refresh_remotes(self) -> List[str]:
10267
return remotes_refreshed
10368

10469

105-
def get_admin_service(request: Request) -> AdminService:
70+
def get_admin_service(
71+
request: Request,
72+
settings: Settings = Depends(get_settings)
73+
) -> AdminService:
10674
"""
10775
Get AdminService instance with dependencies from app state.
10876
10977
Args:
11078
request: FastAPI Request object
79+
settings: Application settings
11180
11281
Returns:
11382
AdminService instance initialized with app state dependencies
83+
84+
Raises:
85+
RuntimeError: If remote reload token is not configured
11486
"""
115-
return AdminService(versions_fetcher=request.app.state.versions_fetcher)
87+
remote_reload_token = settings.remote_reload_token
88+
89+
if remote_reload_token is None:
90+
raise RuntimeError("Remote reload token not configured on server")
91+
92+
return AdminService(
93+
remote_reload_token=remote_reload_token,
94+
versions_fetcher=request.app.state.versions_fetcher
95+
)

0 commit comments

Comments
 (0)