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,
|
HealthCheckComponent,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, disable_auth: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize the BaseActor with an empty ToolCatalog.
|
Initialize the BaseActor with an empty ToolCatalog.
|
||||||
"""
|
"""
|
||||||
self.catalog = ToolCatalog()
|
self.catalog = ToolCatalog()
|
||||||
|
self.disable_auth = disable_auth
|
||||||
|
|
||||||
def get_catalog(self) -> list[ToolDefinition]:
|
def get_catalog(self) -> list[ToolDefinition]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ class Router(ABC):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@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.
|
Add a route to the router.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class HealthCheckComponent(ActorComponent):
|
||||||
"""
|
"""
|
||||||
Register the health check route with the router.
|
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]:
|
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.
|
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
|
Initialize the FastAPIActor with a FastAPI app
|
||||||
instance and an empty ToolCatalog.
|
instance and an empty ToolCatalog.
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__(disable_auth)
|
||||||
self.app = app
|
self.app = app
|
||||||
self.router = FastAPIRouter(app, self)
|
self.router = FastAPIRouter(app, self)
|
||||||
self.register_routes(self.router)
|
self.register_routes(self.router)
|
||||||
|
|
@ -33,14 +33,16 @@ class FastAPIRouter(Router):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.actor = actor
|
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.
|
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(
|
async def wrapped_handler(
|
||||||
request: Request,
|
request: Request,
|
||||||
_: None = Depends(validate_engine_request),
|
_: None = Depends(validate_engine_request) if use_auth_for_route else None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
body_str = await request.body()
|
body_str = await request.body()
|
||||||
body_json = json.loads(body_str) if body_str else {}
|
body_json = json.loads(body_str) if body_str else {}
|
||||||
|
|
@ -56,10 +58,14 @@ class FastAPIRouter(Router):
|
||||||
|
|
||||||
return wrapped_handler
|
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.
|
Add a route to the FastAPI application.
|
||||||
"""
|
"""
|
||||||
self.app.add_api_route(
|
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
|
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.
|
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__
|
handler_name = handler.__name__ if hasattr(handler, "__name__") else type(handler).__name__
|
||||||
endpoint_name = f"actor_{handler_name}_{method}"
|
endpoint_name = f"actor_{handler_name}_{method}"
|
||||||
self.app.add_url_rule(
|
self.app.add_url_rule(
|
||||||
|
|
|
||||||
|
|
@ -263,15 +263,28 @@ def dev(
|
||||||
port: int = typer.Option(
|
port: int = typer.Option(
|
||||||
"8000", "-p", "--port", help="Port for the app, defaults to ", show_default=True
|
"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:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Starts the actor with host, port, and reload options. Uses
|
Starts the actor with host, port, and reload options. Uses
|
||||||
Uvicorn as ASGI actor. Parameters allow runtime configuration.
|
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
|
from arcade.cli.serve import serve_default_actor
|
||||||
|
|
||||||
try:
|
try:
|
||||||
serve_default_actor(host, port)
|
serve_default_actor(host, port, disable_auth)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
console.print("actor stopped by user.", style="bold red")
|
console.print("actor stopped by user.", style="bold red")
|
||||||
typer.Exit()
|
typer.Exit()
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@ from arcade.core.toolkit import Toolkit
|
||||||
console = Console()
|
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.
|
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.",
|
description="Arcade AI default Actor implementation using FastAPI.",
|
||||||
version="0.1.0",
|
version="0.1.0",
|
||||||
)
|
)
|
||||||
actor = FastAPIActor(app)
|
actor = FastAPIActor(app, disable_auth=disable_auth)
|
||||||
for toolkit in toolkits:
|
for toolkit in toolkits:
|
||||||
actor.register_toolkit(toolkit)
|
actor.register_toolkit(toolkit)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue