From a3e9e56eb1ce4f0079d3186fe163537c05becfec Mon Sep 17 00:00:00 2001 From: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com> Date: Mon, 25 Mar 2024 01:01:56 -0700 Subject: [PATCH] Basic Auth Service --- api/poetry.lock | 2 +- api/pyproject.toml | 1 + api/src/main.py | 42 +++++++++++++++++++++++++++++------------ api/src/routers/apps.py | 33 ++++++++++++++++++++++++++++---- 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index 3275945..34d8d27 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -2696,4 +2696,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8.1" -content-hash = "aa7928d0a259d20dd9805ab5b159eebb293eac0b2e46a40d5df55f797222a8f3" +content-hash = "d2b0e968cff39082c16334498a3571c90b10fec28268f89821f13e2764990393" diff --git a/api/pyproject.toml b/api/pyproject.toml index 17c6c60..3b1619f 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -27,6 +27,7 @@ realtime = "^1.0.2" psycopg = {extras = ["binary"], version = "^3.1.18"} langchain = "^0.1.12" langchain-openai = "^0.0.8" +httpx = "^0.27.0" [tool.ruff.lint] # from https://docs.astral.sh/ruff/linter/#rule-selection example diff --git a/api/src/main.py b/api/src/main.py index 167d003..08e567f 100644 --- a/api/src/main.py +++ b/api/src/main.py @@ -1,10 +1,12 @@ import json import logging import os +import re import uuid from contextlib import asynccontextmanager from typing import Optional, Sequence +import httpx import sentry_sdk from fastapi import ( APIRouter, @@ -220,33 +222,49 @@ async def lifespan(app: FastAPI): add_pagination(app) USE_AUTH_SERVICE = os.getenv("USE_AUTH_SERVICE", "False").lower() == "true" +AUTH_SERVICE_URL = os.getenv("AUTH_SERVICE_URL", "http://localhost:8001") -# TODO make the API Token Validation Optional class BearerTokenMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): authorization: Optional[str] = request.headers.get("Authorization") if authorization: scheme, _, token = authorization.partition(" ") - if scheme.lower() == "bearer": - if token == "default": + if scheme.lower() == "bearer" and token: + id_pattern = r"\/apps\/([^\/]+)" + name_pattern = r"\/apps\/name\/([^\/]+)|\/apps\/get_or_create\/([^\/]+)" + match_id = re.search(id_pattern, request.url.path) + match_name = re.search(name_pattern, request.url.path) + payload = {"token": token} + if match_name: + payload["name"] = match_name.group(1) + elif match_id: + payload["app_id"] = match_id.group(1) + + res = httpx.get( + f"{AUTH_SERVICE_URL}/validate", + params=payload, + ) + data = res.json() + if ( + data["app_id"] or data["name"] + ): # Anything that checks app_id if True is valid return await call_next(request) + if data["token"]: + check_pattern = r"^\/apps$|^\/apps\/get_or_create" + match = re.search(check_pattern, request.url.path) + if match: + return await call_next(request) return Response(content="Invalid token.", status_code=400) - # Here you can add your token validation logic - # For example, checking token validity, expiration, etc. - # If the token is valid, you let the request pass through: else: - # If the scheme is not Bearer, you might want to either - # 1. Reject the request - # 2. Ignore and proceed with the next middleware or the request - # This example demonstrates rejecting the request: return Response( content="Invalid authentication scheme.", status_code=400 ) - # If no Authorization header is present, you can choose to reject the request or let it pass - # This example demonstrates rejecting the request: + exclude_paths = ["/docs", "/redoc", "/openapi.json"] + if request.url.path in exclude_paths: + return await call_next(request) return Response(content="Authorization header missing.", status_code=401) diff --git a/api/src/routers/apps.py b/api/src/routers/apps.py index 648c28b..d45f72f 100644 --- a/api/src/routers/apps.py +++ b/api/src/routers/apps.py @@ -1,5 +1,8 @@ +import os import uuid +from typing import Optional +import httpx from fastapi import APIRouter, HTTPException, Request from sqlalchemy.ext.asyncio import AsyncSession @@ -57,8 +60,30 @@ async def create_app(request: Request, app: schemas.AppCreate, db=db): schemas.App: Created App object """ - - return await crud.create_app(db, app=app) + USE_AUTH_SERVICE = os.getenv("USE_AUTH_SERVICE", "False").lower() == "true" + if USE_AUTH_SERVICE: + AUTH_SERVICE_URL = os.getenv("AUTH_SERVICE_URL", "http://localhost:8001") + authorization: Optional[str] = request.headers.get("Authorization") + if authorization: + scheme, _, token = authorization.partition(" ") + if token is not None: + honcho_app = await crud.create_app(db, app=app) + if token == "default": + return honcho_app + res = httpx.put( + f"{AUTH_SERVICE_URL}/organizations", + json={ + "id": str(honcho_app.id), + "name": honcho_app.name, + "token": token, + }, + ) + data = res.json() + if data: + return honcho_app + else: + honcho_app = await crud.create_app(db, app=app) + return honcho_app @router.get("/get_or_create/{name}", response_model=schemas.App) @@ -73,9 +98,9 @@ async def get_or_create_app(request: Request, name: str, db=db): """ print("name", name) - app = await crud.get_app_by_name(db, name=name) + app = await crud.get_app_by_name(db=db, name=name) if app is None: - app = await crud.create_app(db, app=schemas.AppCreate(name=name)) + app = await create_app(request=request, db=db, app=schemas.AppCreate(name=name)) return app