Override Actor auth for easier dev testing (#33)
In this PR: - The Actor health check route now _never_ requires auth (bearer token). It is always unprotected. - `arcade dev --no-auth` disables all Actor auth entirely, making all routes unprotected. Useful for debugging, but emits a warning to the console.
This commit is contained in:
parent
75c6a2becf
commit
c59bb678dd
7 changed files with 42 additions and 13 deletions
|
|
@ -32,11 +32,12 @@ class BaseActor(Actor):
|
|||
HealthCheckComponent,
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, disable_auth: bool = False) -> None:
|
||||
"""
|
||||
Initialize the BaseActor with an empty ToolCatalog.
|
||||
"""
|
||||
self.catalog = ToolCatalog()
|
||||
self.disable_auth = disable_auth
|
||||
|
||||
def get_catalog(self) -> list[ToolDefinition]:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ class Router(ABC):
|
|||
"""
|
||||
|
||||
@abstractmethod
|
||||
def add_route(self, endpoint_path: str, handler: Callable, method: str) -> None:
|
||||
def add_route(
|
||||
self, endpoint_path: str, handler: Callable, method: str, require_auth: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Add a route to the router.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class HealthCheckComponent(ActorComponent):
|
|||
"""
|
||||
Register the health check route with the router.
|
||||
"""
|
||||
router.add_route("health", self, method="GET")
|
||||
router.add_route("health", self, method="GET", require_auth=False)
|
||||
|
||||
async def __call__(self, request: RequestData) -> dict[str, Any]:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ class FastAPIActor(BaseActor):
|
|||
An Arcade Actor that is hosted inside a FastAPI app.
|
||||
"""
|
||||
|
||||
def __init__(self, app: FastAPI) -> None:
|
||||
def __init__(self, app: FastAPI, *, disable_auth: bool = False) -> None:
|
||||
"""
|
||||
Initialize the FastAPIActor with a FastAPI app
|
||||
instance and an empty ToolCatalog.
|
||||
"""
|
||||
super().__init__()
|
||||
super().__init__(disable_auth)
|
||||
self.app = app
|
||||
self.router = FastAPIRouter(app, self)
|
||||
self.register_routes(self.router)
|
||||
|
|
@ -33,14 +33,16 @@ class FastAPIRouter(Router):
|
|||
self.app = app
|
||||
self.actor = actor
|
||||
|
||||
def _wrap_handler(self, handler: Callable) -> Callable:
|
||||
def _wrap_handler(self, handler: Callable, require_auth: bool = True) -> Callable:
|
||||
"""
|
||||
Wrap the handler to handle FastAPI-specific request and response.
|
||||
"""
|
||||
|
||||
use_auth_for_route = not self.actor.disable_auth and require_auth
|
||||
|
||||
async def wrapped_handler(
|
||||
request: Request,
|
||||
_: None = Depends(validate_engine_request),
|
||||
_: None = Depends(validate_engine_request) if use_auth_for_route else None,
|
||||
) -> Any:
|
||||
body_str = await request.body()
|
||||
body_json = json.loads(body_str) if body_str else {}
|
||||
|
|
@ -56,10 +58,14 @@ class FastAPIRouter(Router):
|
|||
|
||||
return wrapped_handler
|
||||
|
||||
def add_route(self, endpoint_path: str, handler: Callable, method: str) -> None:
|
||||
def add_route(
|
||||
self, endpoint_path: str, handler: Callable, method: str, require_auth: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Add a route to the FastAPI application.
|
||||
"""
|
||||
self.app.add_api_route(
|
||||
f"{self.actor.base_path}/{endpoint_path}", self._wrap_handler(handler), methods=[method]
|
||||
f"{self.actor.base_path}/{endpoint_path}",
|
||||
self._wrap_handler(handler, require_auth),
|
||||
methods=[method],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -56,10 +56,15 @@ class FlaskRouter(Router):
|
|||
|
||||
return wrapped_handler
|
||||
|
||||
def add_route(self, endpoint_path: str, handler: Callable, method: str) -> None:
|
||||
def add_route(
|
||||
self, endpoint_path: str, handler: Callable, method: str, require_auth: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Add a route to the Flask application.
|
||||
"""
|
||||
# TODO: Implement auth
|
||||
# use_auth_for_route = not self.actor.disable_auth and require_auth
|
||||
|
||||
handler_name = handler.__name__ if hasattr(handler, "__name__") else type(handler).__name__
|
||||
endpoint_name = f"actor_{handler_name}_{method}"
|
||||
self.app.add_url_rule(
|
||||
|
|
|
|||
|
|
@ -263,15 +263,28 @@ def dev(
|
|||
port: int = typer.Option(
|
||||
"8000", "-p", "--port", help="Port for the app, defaults to ", show_default=True
|
||||
),
|
||||
disable_auth: bool = typer.Option(
|
||||
False,
|
||||
"--no-auth",
|
||||
help="Disable authentication for the actor. Not recommended for production.",
|
||||
show_default=True,
|
||||
),
|
||||
) -> None:
|
||||
"""
|
||||
Starts the actor with host, port, and reload options. Uses
|
||||
Uvicorn as ASGI actor. Parameters allow runtime configuration.
|
||||
"""
|
||||
|
||||
if disable_auth:
|
||||
console.print(
|
||||
"⚠️ Actor authentication is disabled. Not recommended for production.",
|
||||
style="bold yellow",
|
||||
)
|
||||
|
||||
from arcade.cli.serve import serve_default_actor
|
||||
|
||||
try:
|
||||
serve_default_actor(host, port)
|
||||
serve_default_actor(host, port, disable_auth)
|
||||
except KeyboardInterrupt:
|
||||
console.print("actor stopped by user.", style="bold red")
|
||||
typer.Exit()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ from arcade.core.toolkit import Toolkit
|
|||
console = Console()
|
||||
|
||||
|
||||
def serve_default_actor(host: str = "127.0.0.1", port: int = 8000) -> None:
|
||||
def serve_default_actor(
|
||||
host: str = "127.0.0.1", port: int = 8000, disable_auth: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
Get an instance of a FastAPI server with the Arcade Actor.
|
||||
"""
|
||||
|
|
@ -36,7 +38,7 @@ def serve_default_actor(host: str = "127.0.0.1", port: int = 8000) -> None:
|
|||
description="Arcade AI default Actor implementation using FastAPI.",
|
||||
version="0.1.0",
|
||||
)
|
||||
actor = FastAPIActor(app)
|
||||
actor = FastAPIActor(app, disable_auth=disable_auth)
|
||||
for toolkit in toolkits:
|
||||
actor.register_toolkit(toolkit)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue