Skip to content

Commit b9f9665

Browse files
committed
feat: add RBAC module (role-based access control)
1 parent 785685d commit b9f9665

File tree

4 files changed

+44
-51
lines changed

4 files changed

+44
-51
lines changed

app/core/security.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from fastapi import Depends, HTTPException, status
1+
from fastapi import Depends, HTTPException
22
from fastapi.security import OAuth2PasswordBearer
33
from jose import jwt, JWTError
44

@@ -11,13 +11,28 @@ def get_current_user(token: str = Depends(oauth2_scheme)):
1111
try:
1212
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
1313
username: str = payload.get("sub")
14-
if username is None:
14+
role: str = payload.get("role")
15+
16+
if username is None or role is None:
1517
raise HTTPException(status_code=401, detail="Invalid token")
18+
1619
except JWTError:
1720
raise HTTPException(status_code=401, detail="Invalid token")
1821

1922
user = fake_users_db.get(username)
2023
if user is None:
2124
raise HTTPException(status_code=401, detail="User not found")
2225

23-
return user
26+
return {"username": username, "role": role}
27+
28+
29+
def require_role(required_role: str):
30+
def role_checker(current_user=Depends(get_current_user)):
31+
if current_user["role"] != required_role:
32+
raise HTTPException(
33+
status_code=403,
34+
detail=f"Access denied: requires '{required_role}' role"
35+
)
36+
return current_user
37+
38+
return role_checker

app/routers/asset_metadata.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
from fastapi import APIRouter, HTTPException
2-
1+
from fastapi import APIRouter, Depends, HTTPException
32
from app.schemas.asset_metadata import AssetMetadata, CampaignMetadata
4-
from app.services.asset_metadata import (
5-
get_all_assets,
6-
get_asset_by_id,
7-
get_all_campaigns,
8-
)
3+
from app.services.asset_metadata import get_all_assets, get_asset_by_id, get_all_campaigns
4+
from app.core.security import require_role
95

106
router = APIRouter(
117
prefix="/metadata",
128
tags=["Asset Metadata"],
139
)
1410

1511

12+
# ✅ Only ADMIN can view all assets
1613
@router.get("/assets", response_model=list[AssetMetadata])
17-
def list_assets():
14+
def list_assets(current_user=Depends(require_role("admin"))):
1815
return get_all_assets()
1916

2017

18+
# ✅ Only ADMIN can view a single asset
2119
@router.get("/assets/{asset_id}", response_model=AssetMetadata)
22-
def get_asset(asset_id: str):
20+
def get_asset(asset_id: str, current_user=Depends(require_role("admin"))):
2321
asset = get_asset_by_id(asset_id)
2422
if asset is None:
2523
raise HTTPException(status_code=404, detail="Asset not found")
2624
return asset
2725

2826

27+
# ✅ Only ANALYST can view campaign metadata
2928
@router.get("/campaigns", response_model=list[CampaignMetadata])
30-
def list_campaigns():
29+
def list_campaigns(current_user=Depends(require_role("analyst"))):
3130
return get_all_campaigns()

app/routers/auth.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from fastapi import APIRouter, HTTPException, Depends
1+
from fastapi import APIRouter, Depends, HTTPException
22
from fastapi.security import OAuth2PasswordRequestForm
33

4-
from app.schemas.auth import UserLoginRequest, TokenResponse
4+
from app.schemas.auth import TokenResponse
55
from app.services.auth import authenticate_user, create_access_token
66
from app.core.security import get_current_user
77

@@ -17,13 +17,15 @@ def login(form_data: OAuth2PasswordRequestForm = Depends()):
1717
if not user:
1818
raise HTTPException(status_code=401, detail="Invalid username or password")
1919

20-
token = create_access_token({"sub": user["username"]})
20+
# ✅ Token now includes role
21+
token = create_access_token({
22+
"sub": user["username"],
23+
"role": user["role"]
24+
})
25+
2126
return TokenResponse(access_token=token)
2227

2328

2429
@router.get("/me")
25-
def get_me(current_user: dict = Depends(get_current_user)):
26-
return {
27-
"username": current_user["username"],
28-
"role": current_user["role"],
29-
}
30+
def get_me(current_user=Depends(get_current_user)):
31+
return current_user

app/routers/cx_analytics.py

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,15 @@
1-
from fastapi import APIRouter
2-
3-
from app.schemas.cx_analytics import (
4-
AnomalyRequest,
5-
AnomalyResponse,
6-
EventValidationRequest,
7-
EventValidationResponse,
8-
)
9-
from app.services.cx_analytics import detect_anomalies, validate_event_sequence
1+
from fastapi import APIRouter, Depends
2+
from app.schemas.cx_analytics import AnomalyRequest, AnomalyResponse
3+
from app.services.cx_analytics import detect_anomaly
4+
from app.core.security import require_role
105

116
router = APIRouter(
127
prefix="/cx",
138
tags=["CX Analytics"],
149
)
1510

1611

12+
# ✅ Only ANALYST can run anomaly detection
1713
@router.post("/anomaly", response_model=AnomalyResponse)
18-
def anomaly_detection(request: AnomalyRequest):
19-
result = detect_anomalies(request.events, request.expected_events)
20-
21-
return AnomalyResponse(
22-
campaign=request.campaign,
23-
missing_events=result["missing_events"],
24-
unexpected_events=result["unexpected_events"],
25-
anomaly_score=result["anomaly_score"],
26-
status=result["status"],
27-
)
28-
29-
30-
@router.post("/validate-events", response_model=EventValidationResponse)
31-
def validate_events(request: EventValidationRequest):
32-
issues = validate_event_sequence(request.events)
33-
34-
return EventValidationResponse(
35-
session_id=request.session_id,
36-
valid=len(issues) == 0,
37-
issues=issues,
38-
)
14+
def anomaly_detection(request: AnomalyRequest, current_user=Depends(require_role("analyst"))):
15+
return detect_anomaly(request)

0 commit comments

Comments
 (0)