kopia lustrzana https://github.com/bugout-dev/moonstream
User and token endpoints with updated middleware
rodzic
e547b2fb2f
commit
4dd8b4c79b
|
@ -0,0 +1,2 @@
|
|||
|
||||
|
|
@ -2,43 +2,21 @@
|
|||
The Moonstream HTTP API
|
||||
"""
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
import uuid
|
||||
|
||||
from fastapi import (
|
||||
BackgroundTasks,
|
||||
Depends,
|
||||
FastAPI,
|
||||
Form,
|
||||
HTTPException,
|
||||
Path,
|
||||
Query,
|
||||
Request,
|
||||
Response,
|
||||
)
|
||||
from bugout.data import BugoutUser
|
||||
from fastapi import FastAPI, Form
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
from . import data
|
||||
from .settings import DOCS_TARGET_PATH, ORIGINS
|
||||
from .routes.users import app as users_api
|
||||
from .settings import ORIGINS, bugout_client as bc, MOONSTREAM_APPLICATION_ID
|
||||
from .version import MOONSTREAM_VERSION
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
tags_metadata = [{"name": "users", "description": "Operations with users."}]
|
||||
app = FastAPI(openapi_url=None)
|
||||
|
||||
app = FastAPI(
|
||||
title=f"Moonstream API.",
|
||||
description="The Bugout blockchain inspector API.",
|
||||
version=MOONSTREAM_VERSION,
|
||||
openapi_tags=tags_metadata,
|
||||
openapi_url="/openapi.json",
|
||||
docs_url=None,
|
||||
redoc_url=f"/{DOCS_TARGET_PATH}",
|
||||
)
|
||||
|
||||
# CORS settings
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=ORIGINS,
|
||||
|
@ -49,10 +27,13 @@ app.add_middleware(
|
|||
|
||||
|
||||
@app.get("/ping", response_model=data.PingResponse)
|
||||
async def ping() -> data.PingResponse:
|
||||
async def ping_handler() -> data.PingResponse:
|
||||
return data.PingResponse(status="ok")
|
||||
|
||||
|
||||
@app.get("/version", response_model=data.VersionResponse)
|
||||
async def version() -> data.VersionResponse:
|
||||
async def version_handler() -> data.VersionResponse:
|
||||
return data.VersionResponse(version=MOONSTREAM_VERSION)
|
||||
|
||||
|
||||
app.mount("/users", users_api)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import logging
|
||||
from typing import Awaitable, Callable, Dict, List, Optional
|
||||
|
||||
from bugout.data import BugoutUser
|
||||
from bugout.exceptions import BugoutResponseException
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
from fastapi import Request, Response
|
||||
|
||||
from .settings import bugout_client as bc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BroodAuthMiddleware(BaseHTTPMiddleware):
|
||||
"""
|
||||
Checks the authorization header on the request. If it represents a verified Brood user,
|
||||
create another request and get groups user belongs to, after this
|
||||
adds a brood_user attribute to the request.state. Otherwise raises a 403 error.
|
||||
"""
|
||||
|
||||
def __init__(self, app, whitelist: Optional[Dict[str, str]] = None):
|
||||
self.whitelist: Dict[str, str] = {}
|
||||
if whitelist is not None:
|
||||
self.whitelist = whitelist
|
||||
super().__init__(app)
|
||||
|
||||
async def dispatch(
|
||||
self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
|
||||
):
|
||||
path = request.url.path.rstrip("/")
|
||||
method = request.method
|
||||
if path in self.whitelist.keys() and self.whitelist[path] == method:
|
||||
return await call_next(request)
|
||||
|
||||
authorization_header = request.headers.get("authorization")
|
||||
if authorization_header is None:
|
||||
return Response(
|
||||
status_code=403, content="No authorization header passed with request"
|
||||
)
|
||||
user_token_list = authorization_header.split()
|
||||
if len(user_token_list) != 2:
|
||||
return Response(status_code=403, content="Wrong authorization header")
|
||||
user_token: str = user_token_list[-1]
|
||||
|
||||
try:
|
||||
user: BugoutUser = bc.get_user(user_token)
|
||||
if not user.verified:
|
||||
logger.info(
|
||||
f"Attempted journal access by unverified Brood account: {user.id}"
|
||||
)
|
||||
return Response(
|
||||
status_code=403,
|
||||
content="Only verified accounts can access journals",
|
||||
)
|
||||
except BugoutResponseException as e:
|
||||
return Response(status_code=e.status_code, content=e.detail)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing Brood response: {str(e)}")
|
||||
return Response(status_code=500, content="Internal server error")
|
||||
|
||||
request.state.user = user
|
||||
request.state.token = user_token
|
||||
return await call_next(request)
|
|
@ -0,0 +1,108 @@
|
|||
"""
|
||||
The Moonstream users HTTP API
|
||||
"""
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
import uuid
|
||||
|
||||
from bugout.data import BugoutToken, BugoutUser
|
||||
from bugout.exceptions import BugoutResponseException
|
||||
from fastapi import (
|
||||
FastAPI,
|
||||
Form,
|
||||
HTTPException,
|
||||
Request,
|
||||
)
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from ..middleware import BroodAuthMiddleware
|
||||
from ..settings import (
|
||||
MOONSTREAM_APPLICATION_ID,
|
||||
DOCS_TARGET_PATH,
|
||||
ORIGINS,
|
||||
DOCS_PATHS,
|
||||
bugout_client as bc,
|
||||
)
|
||||
from ..version import MOONSTREAM_VERSION
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
tags_metadata = [
|
||||
{"name": "users", "description": "Operations with users."},
|
||||
{"name": "tokens", "description": "Operations with user tokens."},
|
||||
]
|
||||
|
||||
app = FastAPI(
|
||||
title=f"Moonstream API.",
|
||||
description="The Bugout blockchain inspector API.",
|
||||
version=MOONSTREAM_VERSION,
|
||||
openapi_tags=tags_metadata,
|
||||
openapi_url="/openapi.json",
|
||||
docs_url=None,
|
||||
redoc_url=f"/{DOCS_TARGET_PATH}",
|
||||
)
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
whitelist_paths: Dict[str, str] = {}
|
||||
whitelist_paths.update(DOCS_PATHS)
|
||||
whitelist_paths.update({"/users": "POST", "/users/tokens": "POST"})
|
||||
app.add_middleware(BroodAuthMiddleware, whitelist=whitelist_paths)
|
||||
|
||||
|
||||
@app.post("/", tags=["users"], response_model=BugoutUser)
|
||||
async def create_user_handler(
|
||||
username: str = Form(...), email: str = Form(...), password: str = Form(...)
|
||||
) -> BugoutUser:
|
||||
try:
|
||||
user: BugoutUser = bc.create_user(
|
||||
username, email, password, MOONSTREAM_APPLICATION_ID
|
||||
)
|
||||
except BugoutResponseException as e:
|
||||
return HTTPException(status_code=e.status_code, detail=e.detail)
|
||||
except Exception as e:
|
||||
return HTTPException(status_code=500)
|
||||
return user
|
||||
|
||||
|
||||
@app.get("/", tags=["users"], response_model=BugoutUser)
|
||||
async def get_user_handler(request: Request) -> BugoutUser:
|
||||
user: BugoutUser = request.state.user
|
||||
if str(user.application_id) != str(MOONSTREAM_APPLICATION_ID):
|
||||
raise HTTPException(
|
||||
status_code=403, detail="User does not belong to this application"
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
@app.post("/tokens", tags=["tokens"], response_model=BugoutToken)
|
||||
async def login_handler(
|
||||
username: str = Form(...), password: str = Form(...)
|
||||
) -> BugoutToken:
|
||||
try:
|
||||
token: BugoutToken = bc.create_token(
|
||||
username, password, MOONSTREAM_APPLICATION_ID
|
||||
)
|
||||
except BugoutResponseException as e:
|
||||
return HTTPException(status_code=e.status_code)
|
||||
except Exception as e:
|
||||
return HTTPException(status_code=500)
|
||||
return token
|
||||
|
||||
|
||||
@app.delete("/tokens", tags=["tokens"], response_model=uuid.UUID)
|
||||
async def logout_handler(request: Request) -> uuid.UUID:
|
||||
token = request.state.token
|
||||
try:
|
||||
token_id: uuid.UUID = bc.revoke_token(token)
|
||||
except BugoutResponseException as e:
|
||||
return HTTPException(status_code=e.status_code, detail=e.detail)
|
||||
except Exception as e:
|
||||
return HTTPException(status_code=500)
|
||||
return token_id
|
|
@ -1,10 +1,24 @@
|
|||
import os
|
||||
|
||||
from bugout.app import Bugout
|
||||
|
||||
# Bugout
|
||||
# TODO(kompotkot): CHANGE TO PROD!!!!!!!
|
||||
bugout_client = Bugout("http://127.0.0.1:7474", "http://127.0.0.1:7475")
|
||||
|
||||
MOONSTREAM_APPLICATION_ID = os.environ.get("MOONSTREAM_APPLICATION_ID")
|
||||
if MOONSTREAM_APPLICATION_ID is None:
|
||||
raise ValueError("MOONSTREAM_APPLICATION_ID environment variable must be set")
|
||||
|
||||
MOONSTREAM_DATA_JOURNAL_ID = os.environ.get("MOONSTREAM_DATA_JOURNAL_ID")
|
||||
if MOONSTREAM_DATA_JOURNAL_ID is None:
|
||||
raise ValueError("MOONSTREAM_DATA_JOURNAL_ID environment variable must be set")
|
||||
|
||||
# Origin
|
||||
RAW_ORIGIN = os.environ.get("MOONSTREAM_CORS_ALLOWED_ORIGINS")
|
||||
if RAW_ORIGIN is None:
|
||||
raise ValueError(
|
||||
"MOONSTREAM_CORS_ALLOWED_ORIGINS environment variable must be set (comma-separated list of CORS allowed origins"
|
||||
"MOONSTREAM_CORS_ALLOWED_ORIGINS environment variable must be set (comma-separated list of CORS allowed origins)"
|
||||
)
|
||||
ORIGINS = RAW_ORIGIN.split(",")
|
||||
|
||||
|
@ -14,3 +28,8 @@ MOONSTREAM_OPENAPI_LIST = []
|
|||
MOONSTREAM_OPENAPI_LIST_RAW = os.environ.get("MOONSTREAM_OPENAPI_LIST")
|
||||
if MOONSTREAM_OPENAPI_LIST_RAW is not None:
|
||||
MOONSTREAM_OPENAPI_LIST = MOONSTREAM_OPENAPI_LIST_RAW.split(",")
|
||||
|
||||
DOCS_PATHS = {}
|
||||
for path in MOONSTREAM_OPENAPI_LIST:
|
||||
DOCS_PATHS[f"/{path}/{DOCS_TARGET_PATH}"] = "GET"
|
||||
DOCS_PATHS[f"/{path}/{DOCS_TARGET_PATH}/openapi.json"] = "GET"
|
||||
|
|
|
@ -3,7 +3,7 @@ asgiref==3.4.1
|
|||
black==21.7b0
|
||||
boto3==1.18.1
|
||||
botocore==1.21.1
|
||||
bugout==0.1.12
|
||||
bugout==0.1.13
|
||||
certifi==2021.5.30
|
||||
charset-normalizer==2.0.3
|
||||
click==8.0.1
|
||||
|
@ -14,9 +14,9 @@ jmespath==0.10.0
|
|||
mypy==0.910
|
||||
mypy-extensions==0.4.3
|
||||
pathspec==0.9.0
|
||||
pkg-resources==0.0.0
|
||||
pydantic==1.8.2
|
||||
python-dateutil==2.8.2
|
||||
python-multipart-0.0.5
|
||||
regex==2021.7.6
|
||||
requests==2.26.0
|
||||
s3transfer==0.5.0
|
|
@ -1,2 +1,4 @@
|
|||
export MOONSTREAM_CORS_ALLOWED_ORIGINS="http://localhost:3000,https://moonstream.to"
|
||||
export MOONSTREAM_OPENAPI_LIST=""
|
||||
export MOONSTREAM_OPENAPI_LIST="subscriptions"
|
||||
export MOONSTREAM_APPLICATION_ID="<issued_bugout_application_id>"
|
||||
export MOONSTREAM_DATA_JOURNAL_ID="<bugout_journal_id_to_store_blockchain_data>"
|
||||
|
|
Ładowanie…
Reference in New Issue