bibibi12345 commited on
Commit
5b8c4f9
·
1 Parent(s): c9e3eb0

huggginface auth fix and fixed model list

Browse files
Files changed (3) hide show
  1. app/auth.py +90 -26
  2. app/config.py +4 -0
  3. app/routes/models_api.py +7 -2
app/auth.py CHANGED
@@ -1,7 +1,10 @@
1
  from fastapi import HTTPException, Header, Depends
2
  from fastapi.security import APIKeyHeader
3
  from typing import Optional
4
- from config import API_KEY # Import API_KEY directly for use in local validation
 
 
 
5
 
6
  # Function to validate API key (moved from config.py)
7
  def validate_api_key(api_key_to_validate: str) -> bool:
@@ -18,28 +21,89 @@ def validate_api_key(api_key_to_validate: str) -> bool:
18
  api_key_header = APIKeyHeader(name="Authorization", auto_error=False)
19
 
20
  # Dependency for API key validation
21
- async def get_api_key(authorization: Optional[str] = Header(None)):
22
- if authorization is None:
23
- raise HTTPException(
24
- status_code=401,
25
- detail="Missing API key. Please include 'Authorization: Bearer YOUR_API_KEY' header."
26
- )
27
-
28
- # Check if the header starts with "Bearer "
29
- if not authorization.startswith("Bearer "):
30
- raise HTTPException(
31
- status_code=401,
32
- detail="Invalid API key format. Use 'Authorization: Bearer YOUR_API_KEY'"
33
- )
34
-
35
- # Extract the API key
36
- api_key = authorization.replace("Bearer ", "")
37
-
38
- # Validate the API key
39
- if not validate_api_key(api_key): # Call local validate_api_key
40
- raise HTTPException(
41
- status_code=401,
42
- detail="Invalid API key"
43
- )
44
-
45
- return api_key
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi import HTTPException, Header, Depends
2
  from fastapi.security import APIKeyHeader
3
  from typing import Optional
4
+ from config import API_KEY, HUGGINGFACE_API_KEY, HUGGINGFACE # Import API_KEY, HUGGINGFACE_API_KEY, HUGGINGFACE
5
+ import os
6
+ import json
7
+ import base64
8
 
9
  # Function to validate API key (moved from config.py)
10
  def validate_api_key(api_key_to_validate: str) -> bool:
 
21
  api_key_header = APIKeyHeader(name="Authorization", auto_error=False)
22
 
23
  # Dependency for API key validation
24
+ async def get_api_key(
25
+ authorization: Optional[str] = Header(None),
26
+ x_ip_token: Optional[str] = Header(None, alias="x-ip-token")
27
+ ):
28
+ # Check if Hugging Face auth is enabled
29
+ if HUGGINGFACE: # Use HUGGINGFACE from config
30
+ if x_ip_token is None:
31
+ raise HTTPException(
32
+ status_code=401, # Unauthorised - because x-ip-token is missing
33
+ detail="Missing x-ip-token header. This header is required for Hugging Face authentication."
34
+ )
35
+
36
+ try:
37
+ # Decode JWT payload
38
+ parts = x_ip_token.split('.')
39
+ if len(parts) < 2:
40
+ raise ValueError("Invalid JWT format: Not enough parts to extract payload.")
41
+ payload_encoded = parts[1]
42
+ # Add padding if necessary, as Python's base64.urlsafe_b64decode requires it
43
+ payload_encoded += '=' * (-len(payload_encoded) % 4)
44
+ decoded_payload_bytes = base64.urlsafe_b64decode(payload_encoded)
45
+ payload = json.loads(decoded_payload_bytes.decode('utf-8'))
46
+ except ValueError as ve:
47
+ # Log server-side for debugging, but return a generic client error
48
+ print(f"ValueError processing x-ip-token: {ve}")
49
+ raise HTTPException(status_code=400, detail=f"Invalid JWT format in x-ip-token: {str(ve)}")
50
+ except (json.JSONDecodeError, base64.binascii.Error, UnicodeDecodeError) as e:
51
+ print(f"Error decoding/parsing x-ip-token payload: {e}")
52
+ raise HTTPException(status_code=400, detail=f"Malformed x-ip-token payload: {str(e)}")
53
+ except Exception as e: # Catch any other unexpected errors during token processing
54
+ print(f"Unexpected error processing x-ip-token: {e}")
55
+ raise HTTPException(status_code=500, detail="Internal error processing x-ip-token.")
56
+
57
+ error_in_token = payload.get("error")
58
+
59
+ if error_in_token == "InvalidAccessToken":
60
+ raise HTTPException(
61
+ status_code=403,
62
+ detail="Access denied: x-ip-token indicates 'InvalidAccessToken'."
63
+ )
64
+ elif error_in_token is None: # JSON 'null' is Python's None
65
+ # If error is null, auth is successful. Now check if HUGGINGFACE_API_KEY is configured.
66
+ if not HUGGINGFACE_API_KEY: # Check if the key is empty or not set.
67
+ print("Security configuration error: HUGGINGFACE_API_KEY is not configured, but HUGGINGFACE mode is active and x-ip-token is valid.")
68
+ raise HTTPException(
69
+ status_code=500,
70
+ detail="Service security configuration incomplete: HuggingFace API Key not set."
71
+ )
72
+ print(f"HuggingFace authentication successful via x-ip-token (error field was null).")
73
+ return HUGGINGFACE_API_KEY # Return the configured HUGGINGFACE_API_KEY
74
+ else:
75
+ # Any other non-null, non-"InvalidAccessToken" value in 'error' field
76
+ raise HTTPException(
77
+ status_code=403,
78
+ detail=f"Access denied: x-ip-token indicates an unhandled error: '{error_in_token}'."
79
+ )
80
+ else:
81
+ # Fallback to Bearer token authentication if HUGGINGFACE env var is not "true"
82
+ if authorization is None:
83
+ detail_message = "Missing API key. Please include 'Authorization: Bearer YOUR_API_KEY' header."
84
+ # Optionally, provide a hint if the HUGGINGFACE env var exists but is not "true"
85
+ if os.getenv("HUGGINGFACE") is not None: # Check for existence, not value
86
+ detail_message += " (Note: HUGGINGFACE mode with x-ip-token is not currently active)."
87
+ raise HTTPException(
88
+ status_code=401,
89
+ detail=detail_message
90
+ )
91
+
92
+ # Check if the header starts with "Bearer "
93
+ if not authorization.startswith("Bearer "):
94
+ raise HTTPException(
95
+ status_code=401,
96
+ detail="Invalid API key format. Use 'Authorization: Bearer YOUR_API_KEY'"
97
+ )
98
+
99
+ # Extract the API key
100
+ api_key = authorization.replace("Bearer ", "")
101
+
102
+ # Validate the API key
103
+ if not validate_api_key(api_key): # Call local validate_api_key
104
+ raise HTTPException(
105
+ status_code=401,
106
+ detail="Invalid API key"
107
+ )
108
+
109
+ return api_key
app/config.py CHANGED
@@ -6,6 +6,10 @@ DEFAULT_PASSWORD = "123456"
6
  # Get password from environment variable or use default
7
  API_KEY = os.environ.get("API_KEY", DEFAULT_PASSWORD)
8
 
 
 
 
 
9
  # Directory for service account credential files
10
  CREDENTIALS_DIR = os.environ.get("CREDENTIALS_DIR", "/app/credentials")
11
 
 
6
  # Get password from environment variable or use default
7
  API_KEY = os.environ.get("API_KEY", DEFAULT_PASSWORD)
8
 
9
+ # HuggingFace Authentication Settings
10
+ HUGGINGFACE = os.environ.get("HUGGINGFACE", "false").lower() == "true"
11
+ HUGGINGFACE_API_KEY = os.environ.get("HUGGINGFACE_API_KEY", "") # Default to empty string, auth logic will verify if HF_MODE is true and this key is needed
12
+
13
  # Directory for service account credential files
14
  CREDENTIALS_DIR = os.environ.get("CREDENTIALS_DIR", "/app/credentials")
15
 
app/routes/models_api.py CHANGED
@@ -25,6 +25,7 @@ async def list_models(fastapi_request: Request, api_key: str = Depends(get_api_k
25
  raw_express_models = await get_vertex_express_models()
26
 
27
  candidate_model_ids = set()
 
28
 
29
  if has_express_key:
30
  candidate_model_ids.update(raw_express_models)
@@ -57,8 +58,12 @@ async def list_models(fastapi_request: Request, api_key: str = Depends(get_api_k
57
  for original_model_id in sorted(list(all_model_ids)):
58
  current_display_prefix = ""
59
  # Only add PAY_PREFIX if the model is not already an EXPRESS model (which has its own prefix)
60
- if not original_model_id.startswith("[EXPRESS]") and \
61
- has_sa_creds and not has_express_key and EXPERIMENTAL_MARKER not in original_model_id:
 
 
 
 
62
  current_display_prefix = PAY_PREFIX
63
 
64
  base_display_id = f"{current_display_prefix}{original_model_id}"
 
25
  raw_express_models = await get_vertex_express_models()
26
 
27
  candidate_model_ids = set()
28
+ raw_vertex_models_set = set(raw_vertex_models) # For checking origin during prefixing
29
 
30
  if has_express_key:
31
  candidate_model_ids.update(raw_express_models)
 
58
  for original_model_id in sorted(list(all_model_ids)):
59
  current_display_prefix = ""
60
  # Only add PAY_PREFIX if the model is not already an EXPRESS model (which has its own prefix)
61
+ # Apply PAY_PREFIX if SA creds are present, it's a model from raw_vertex_models,
62
+ # it's not experimental, and not already an EXPRESS model.
63
+ if has_sa_creds and \
64
+ original_model_id in raw_vertex_models_set and \
65
+ EXPERIMENTAL_MARKER not in original_model_id and \
66
+ not original_model_id.startswith("[EXPRESS]"):
67
  current_display_prefix = PAY_PREFIX
68
 
69
  base_display_id = f"{current_display_prefix}{original_model_id}"