Skip to content

Commit 6903803

Browse files
committed
Fix bug with public data network.
1 parent 1fc82ab commit 6903803

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

FLEXIBLE_AUTH_README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,18 @@ All historical data endpoints now support both authentication methods:
8989

9090
## Authentication Flow
9191

92-
### Device API Key Flow
92+
### Public Device Flow (No Authentication Required)
93+
1. System checks if the requested device is marked as public (`private_data=False`)
94+
2. If public, data is returned immediately without authentication
95+
3. This preserves the original behavior for public devices
96+
97+
### Private Device - API Key Flow
9398
1. User provides API key in `X-API-Key` header
9499
2. System validates API key exists in database
95100
3. System checks if API key is authorized for requested device
96101
4. If authorized, data is returned
97102

98-
### Fief Token Flow (with caching)
103+
### Private Device - Fief Token Flow (with caching)
99104
1. User provides token in `Authorization: Bearer` header
100105
2. System checks Redis cache for token validation
101106
3. If cache miss, validates with Fief server and caches result

opensensor/collection_apis.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from opensensor.users import (
3535
AuthInfo,
3636
User,
37-
flexible_auth,
37+
flexible_auth_optional,
3838
migration_complete,
3939
validate_device_access_flexible,
4040
validate_environment,
@@ -653,7 +653,7 @@ def sample_and_paginate_collection(
653653

654654
def create_historical_data_route(entity: Type[T]):
655655
async def historical_data_route(
656-
auth_info: AuthInfo = Depends(flexible_auth),
656+
auth_info: Optional[AuthInfo] = Depends(flexible_auth_optional),
657657
device_id: str = Path(
658658
title="The ID of the device chain for which to retrieve historical data."
659659
),
@@ -664,10 +664,11 @@ async def historical_data_route(
664664
size: int = Query(50, ge=1, le=1000, description="Page size"),
665665
unit: str | None = Query(None, description="Unit"),
666666
) -> Page[T]:
667-
# Use flexible authentication validation
667+
# Use flexible authentication validation (handles public devices)
668668
if not validate_device_access_flexible(auth_info, device_id):
669-
auth_type = auth_info.auth_type
670-
if auth_type == "fief":
669+
if auth_info is None:
670+
detail = f"Device {device_id} is private and requires authentication"
671+
elif auth_info.auth_type == "fief":
671672
detail = f"User is not authorized to access device {device_id}"
672673
else:
673674
detail = f"API key is not authorized to access device {device_id}"

opensensor/users.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,50 @@ class AuthInfo(BaseModel):
531531
api_key_info: Optional[APIKey] = None
532532

533533

534+
def is_device_public(device_id: str) -> bool:
535+
"""
536+
Check if a device is public (doesn't require authentication).
537+
"""
538+
device_id_part, device_name = get_device_parts(device_id)
539+
api_keys, owner = get_api_keys_by_device_id(device_id)
540+
541+
if len(api_keys) == 0:
542+
# No API keys found - device doesn't exist or is not configured
543+
return False
544+
545+
# Check if any API key for this device is marked as public (private_data=False)
546+
for api_key in api_keys:
547+
if api_key.device_id == device_id_part and api_key.device_name == device_name:
548+
if not api_key.private_data:
549+
return True
550+
551+
return False
552+
553+
554+
async def flexible_auth_optional(
555+
fief_user: Optional[FiefUserInfo] = Depends(get_cached_fief_user_optional()),
556+
api_key: Optional[str] = Header(None, alias="X-API-Key"),
557+
) -> Optional[AuthInfo]:
558+
"""
559+
Optional flexible authentication that accepts either Fief tokens or device API keys.
560+
Returns None if no authentication is provided (for public device access).
561+
"""
562+
if fief_user:
563+
# Fief token authentication (now cached)
564+
return AuthInfo(auth_type="fief", fief_user=fief_user)
565+
elif api_key:
566+
# API key authentication
567+
user_and_key = get_user_by_api_key(api_key)
568+
if user_and_key is None:
569+
raise HTTPException(status_code=403, detail="Invalid API key")
570+
571+
user, api_key_info = user_and_key
572+
return AuthInfo(auth_type="api_key", user=user, api_key_info=api_key_info)
573+
else:
574+
# No authentication provided - this is OK for public devices
575+
return None
576+
577+
534578
async def flexible_auth(
535579
fief_user: Optional[FiefUserInfo] = Depends(get_cached_fief_user_optional()),
536580
api_key: Optional[str] = Header(None, alias="X-API-Key"),
@@ -554,10 +598,19 @@ async def flexible_auth(
554598
raise HTTPException(status_code=401, detail="Authentication required")
555599

556600

557-
def validate_device_access_flexible(auth_info: AuthInfo, device_id: str) -> bool:
601+
def validate_device_access_flexible(auth_info: Optional[AuthInfo], device_id: str) -> bool:
558602
"""
559603
Validate device access for flexible authentication.
604+
Handles public devices (no auth required) and private devices (auth required).
560605
"""
606+
# First check if the device is public
607+
if is_device_public(device_id):
608+
return True
609+
610+
# Device is private, authentication is required
611+
if auth_info is None:
612+
return False
613+
561614
if auth_info.auth_type == "fief":
562615
# Use existing Fief-based validation
563616
return device_id_is_allowed_for_user(device_id, user=auth_info.fief_user)

0 commit comments

Comments
 (0)