Files
tabbyAPI/auth.py
kingbri 8fa764bfbe Auth: Add option to disable authentication
This creates a massive security hole, but it's gated behind a flag
for users who only use localhost.

A warning will pop up when users disable authentication.

Signed-off-by: kingbri <bdashore3@proton.me>
2023-12-21 23:40:16 -05:00

106 lines
3.5 KiB
Python

import secrets
import yaml
from fastapi import Header, HTTPException
from pydantic import BaseModel
from typing import Optional
"""
This method of authorization is pretty insecure, but since TabbyAPI is a local
application, it should be fine.
"""
class AuthKeys(BaseModel):
api_key: str
admin_key: str
def verify_key(self, test_key: str, key_type: str):
# Match statements are only available in python 3.10 and up
if key_type == "admin_key":
return test_key == self.admin_key
elif key_type == "api_key":
# Admin keys are valid for all API calls
return test_key == self.api_key or test_key == self.admin_key
else:
return False
auth_keys: Optional[AuthKeys] = None
disable_auth: bool = False
def load_auth_keys(disable_from_config: bool):
global auth_keys
global disable_auth
disable_auth = disable_from_config
if disable_from_config:
print(
"!! Warning: Disabling authentication makes your instance vulnerable.",
"Set the \"disable_auth\" flag to False in config.yml if you want to share this",
"instance with others."
)
return
try:
with open("api_tokens.yml", "r", encoding = 'utf8') as auth_file:
auth_keys_dict = yaml.safe_load(auth_file)
auth_keys = AuthKeys.model_validate(auth_keys_dict)
except OSError:
new_auth_keys = AuthKeys(
api_key = secrets.token_hex(16),
admin_key = secrets.token_hex(16)
)
auth_keys = new_auth_keys
with open("api_tokens.yml", "w", encoding = "utf8") as auth_file:
yaml.safe_dump(auth_keys.model_dump(), auth_file, default_flow_style=False)
print(
f"Your API key is: {auth_keys.api_key}\n"
f"Your admin key is: {auth_keys.admin_key}\n\n"
"If these keys get compromised, make sure to delete api_tokens.yml and restart the server. Have fun!"
)
def check_api_key(x_api_key: str = Header(None), authorization: str = Header(None)):
# Allow request if auth is disabled
if disable_auth:
return
if x_api_key:
if auth_keys.verify_key(x_api_key, "api_key"):
return x_api_key
else:
raise HTTPException(401, "Invalid API key")
elif authorization:
split_key = authorization.split(" ")
if len(split_key) < 2:
raise HTTPException(401, "Invalid API key")
elif split_key[0].lower() == "bearer" and auth_keys.verify_key(split_key[1], "api_key"):
return authorization
else:
raise HTTPException(401, "Invalid API key")
else:
raise HTTPException(401, "Please provide an API key")
def check_admin_key(x_admin_key: str = Header(None), authorization: str = Header(None)):
# Allow request if auth is disabled
if disable_auth:
return
if x_admin_key:
if auth_keys.verify_key(x_admin_key, "admin_key"):
return x_admin_key
else:
raise HTTPException(401, "Invalid admin key")
elif authorization:
split_key = authorization.split(" ")
if len(split_key) < 2:
raise HTTPException(401, "Invalid admin key")
elif split_key[0].lower() == "bearer" and auth_keys.verify_key(split_key[1], "admin_key"):
return authorization
else:
raise HTTPException(401, "Invalid admin key")
else:
raise HTTPException(401, "Please provide an admin key")