Skip to content

Commit 84f37ef

Browse files
authored
Adding project APIs and Auth APIs (#23)
* token creation init * fixed JSON issues * fixed lint issues * token is generating * added method to get claims * added api-server * added more apis * fixed authentication * fixing linting issues
1 parent 25fe348 commit 84f37ef

10 files changed

Lines changed: 138 additions & 52 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import jwt
2+
import os
3+
import time
4+
5+
from app.singletons.logs_manager import LogsManager
6+
7+
from .models.token_claims import TokenClaims
8+
9+
logger = LogsManager().get_logger()
10+
11+
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY")
12+
if not JWT_SECRET_KEY:
13+
raise ValueError("JWT_SECRET_KEY environment variable is not set or is empty.")
14+
JWT_ALGORITHM = "HS256"
15+
16+
17+
async def get_token_claims(token: str, x_exosphere_request_id: str):
18+
try:
19+
claims = TokenClaims(**jwt.decode(token, JWT_SECRET_KEY, algorithms=[JWT_ALGORITHM]))
20+
logger.info("Token claims decoded", x_exosphere_request_id=x_exosphere_request_id, user_id=claims.user_id)
21+
22+
if claims.exp < time.time():
23+
logger.error("Token expired", x_exosphere_request_id=x_exosphere_request_id, user_id=claims.user_id)
24+
return None
25+
26+
return claims
27+
28+
except Exception as e:
29+
logger.error("Error decoding token", error=e, x_exosphere_request_id=x_exosphere_request_id)
30+
return None
31+

api-server/app/auth/middlewares/get_token_claims.py

Lines changed: 0 additions & 43 deletions
This file was deleted.

api-server/app/auth/routes.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from fastapi import APIRouter, status, Request
2+
3+
from .controllers.create_token import create_token
4+
from .models.token_request import TokenRequest
5+
from .models.token_response import TokenResponse
6+
7+
router = APIRouter(prefix="/v0/auth", tags=["auth"])
8+
9+
@router.post(
10+
"/token",
11+
response_model=TokenResponse,
12+
status_code=status.HTTP_200_OK,
13+
response_description="Token created successfully"
14+
)
15+
async def create_token_route(body: TokenRequest, request: Request):
16+
x_exosphere_request_id = getattr(request.state, "x_exosphere_request_id", None)
17+
return await create_token(body, x_exosphere_request_id)

api-server/app/main.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@
1717
UnhandledExceptionsMiddleware,
1818
)
1919
from .middlewares.request_id_middleware import RequestIdMiddleware
20-
from .auth.middlewares.get_token_claims import GetTokenClaimsMiddleware
2120

2221
# injecting databases
2322
from .user.models.user_database_model import User
2423
from .project.models.project_database_model import Project
2524

2625
# injecting routers
2726
from .user.routes import router as user_router
28-
from .auth.router import router as auth_router
29-
27+
from .auth.routes import router as auth_router
28+
from .project.routes import router as project_router
29+
3030
load_dotenv()
3131

3232

@@ -54,8 +54,6 @@ async def lifespan(app: FastAPI):
5454
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
5555

5656
# this middleware should be the first one
57-
app.add_middleware(GetTokenClaimsMiddleware)
58-
5957
app.add_middleware(RequestIdMiddleware)
6058

6159
app.add_middleware(UnhandledExceptionsMiddleware)
@@ -69,3 +67,4 @@ def health() -> dict:
6967
# injecting routers
7068
app.include_router(user_router)
7169
app.include_router(auth_router)
70+
app.include_router(project_router)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from beanie import Link
2+
from bson import ObjectId
3+
4+
from ..models.create_project_request import CreateProjectRequest
5+
from ..models.create_project_response import CreateProjectResponse
6+
from ..models.project_database_model import Project
7+
8+
from app.singletons.logs_manager import LogsManager
9+
from app.auth.models.token_claims import TokenClaims
10+
from app.user.models.user_database_model import User
11+
12+
logger = LogsManager().get_logger()
13+
14+
15+
async def create_project(request: CreateProjectRequest, claims: TokenClaims, x_exosphere_request_id: str) -> CreateProjectResponse:
16+
try:
17+
logger.info("Creating new project", x_exosphere_request_id=x_exosphere_request_id, user_id=claims.user_id)
18+
project = Project(
19+
name=request.name,
20+
super_admin=Link(ObjectId(claims.user_id), User)
21+
)
22+
await project.save()
23+
print(project)
24+
return CreateProjectResponse(
25+
id=str(project.id),
26+
name=project.name,
27+
status=project.status,
28+
billing_account=project.billing_account,
29+
super_admin=claims.user_id,
30+
created_at=project.created_at,
31+
updated_at=project.updated_at
32+
)
33+
34+
except Exception as e:
35+
logger.error("Error creating new project", x_exosphere_request_id=x_exosphere_request_id, error=e)
36+
raise e

api-server/app/project/models/billing_account.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
class TaxInformationTypeEnum(str, Enum):
6+
NOT_DEFINED = "NOT_DEFINED"
67
GST = "GST"
78
VAT = "VAT"
89
EIN = "EIN"
@@ -15,7 +16,7 @@ class BillingAccount(BaseModel):
1516

1617
billing_address: str = Field(default="", description="Billing address of the company")
1718

18-
tax_number_type: TaxInformationTypeEnum = Field(default="", description="Type of tax information")
19+
tax_number_type: TaxInformationTypeEnum = Field(default=TaxInformationTypeEnum.NOT_DEFINED, description="Type of tax information")
1920

2021
tax_number: str = Field(default="", description="Tax information of the company")
2122

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from pydantic import BaseModel
2+
3+
class CreateProjectRequest(BaseModel):
4+
name: str
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from pydantic import BaseModel
2+
from .billing_account import BillingAccount
3+
from .project_status_enum import ProjectStatusEnum
4+
from datetime import datetime
5+
6+
7+
class CreateProjectResponse(BaseModel):
8+
id: str
9+
name: str
10+
status: ProjectStatusEnum
11+
billing_account: BillingAccount
12+
super_admin: str
13+
created_at: datetime
14+
updated_at: datetime

api-server/app/project/routes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from fastapi import APIRouter, status, Request, Depends
2+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
3+
from typing import Annotated
4+
from starlette.responses import JSONResponse
5+
6+
from .controllers.create_project import create_project
7+
from .models.create_project_request import CreateProjectRequest
8+
from .models.create_project_response import CreateProjectResponse
9+
10+
from app.auth.get_token_claims import get_token_claims
11+
12+
router = APIRouter(prefix="/v0/project", tags=["project"])
13+
14+
@router.post(
15+
"/",
16+
response_model=CreateProjectResponse,
17+
status_code=status.HTTP_201_CREATED,
18+
response_description="Project created successfully"
19+
)
20+
async def create_project_route(body: CreateProjectRequest, request: Request, token: Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer())]):
21+
x_exosphere_request_id = getattr(request.state, "x_exosphere_request_id", None)
22+
claims = await get_token_claims(token.credentials, x_exosphere_request_id)
23+
24+
if claims is None:
25+
return JSONResponse(status_code=401, content={"message": "Invalid token", "success": False})
26+
27+
return await create_project(body, claims, x_exosphere_request_id)

api-server/app/user/routes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
from .controllers.create_user import create_user
44
from .models.create_user_request import CreateUserRequest
55
from .models.create_user_response import CreateUserResponse
6-
7-
router = APIRouter(prefix="/user", tags=["user"])
6+
7+
router = APIRouter(prefix="/v0/user", tags=["user"])
88

99
@router.post(
10-
"/create",
10+
"/",
1111
response_model=CreateUserResponse,
1212
status_code=status.HTTP_201_CREATED,
1313
response_description="User created successfully"

0 commit comments

Comments
 (0)