Skip to content

Commit e89d4f4

Browse files
committed
feat: Implement RBAC - Backend role field and management endpoints
1 parent ed26660 commit e89d4f4

File tree

6 files changed

+41
-4
lines changed

6 files changed

+41
-4
lines changed

backend/admin.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,27 @@ def get_recent_users(
6969
"joined": u.created_at.strftime("%Y-%m-%d") if u.created_at else "2024-01-01"
7070
})
7171
return safe_users
72+
return safe_users
73+
74+
class RoleUpdate(models.Base):
75+
# This is a Pydantic model hack, better to import from schemas but avoiding circular import issues easily
76+
pass
77+
78+
@router.put("/users/{user_id}/role")
79+
def update_user_role(
80+
user_id: int,
81+
role: str,
82+
db: Session = Depends(database.get_db),
83+
admin: models.User = Depends(get_current_admin)
84+
):
85+
"""Update a user's system role (patient, doctor, admin)."""
86+
user = db.query(models.User).filter(models.User.id == user_id).first()
87+
if not user:
88+
raise HTTPException(status_code=404, detail="User not found")
89+
90+
if role not in ["patient", "doctor", "admin"]:
91+
raise HTTPException(status_code=400, detail="Invalid role. Must be patient, doctor, or admin.")
92+
93+
user.role = role
94+
db.commit()
95+
return {"status": "success", "message": f"User {user.username} promoted to {role}"}

backend/auth.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(
8585

8686
def is_admin(user: models.User) -> bool:
8787
"""Check if user has admin privileges."""
88-
# Check both username='admin' for backward compatibility and future role field
89-
return user.username == "admin" or getattr(user, 'is_admin', False)
88+
# Check both username='admin' for backward compatibility and role field
89+
return user.username == "admin" or user.username.startswith("admin_") or getattr(user, 'role', 'patient') == 'admin'
9090

9191
# --- Endpoints ---
9292

@@ -198,7 +198,8 @@ def get_user_profile(current_user: models.User = Depends(get_current_user)) -> D
198198
"activity_level": current_user.activity_level,
199199
"sleep_hours": current_user.sleep_hours,
200200
"stress_level": current_user.stress_level,
201-
"allow_data_collection": bool(current_user.allow_data_collection)
201+
"allow_data_collection": bool(current_user.allow_data_collection),
202+
"role": getattr(current_user, "role", "patient")
202203
}
203204

204205
@router.put("/profile")

backend/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def run_migrations():
4545
"plan_tier": "VARCHAR",
4646
"subscription_expiry": "TIMESTAMP", # Changed from DATETIME for Postgres compat
4747
"razorpay_customer_id": "VARCHAR",
48-
"created_at": "TIMESTAMP"
48+
"created_at": "TIMESTAMP",
49+
"role": "VARCHAR"
4950
}
5051

5152
try:

backend/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class User(Base):
1010
username = Column(String, unique=True, index=True)
1111
hashed_password = Column(String)
1212
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
13+
role = Column(String, default="patient") # patient, doctor, admin
1314

1415
# Profile Data
1516
email = Column(String, nullable=True)

backend/schemas.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class UserResponse(BaseModel):
2020
"""Schema for Public User Profile"""
2121
id: int
2222
username: str
23+
role: Optional[str] = "patient"
2324
full_name: Optional[str] = None
2425
email: Optional[str] = None
2526

frontend/utils/api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ def login(username, password) -> bool:
8080
st.session_state['token'] = token
8181
st.session_state['username'] = username
8282
save_session(token, username)
83+
84+
# Fetch profile to get Role
85+
try:
86+
prof = fetch_profile()
87+
if prof:
88+
st.session_state['role'] = prof.get('role', 'patient')
89+
except:
90+
pass
91+
8392
return True
8493
detail = resp.json().get('detail', 'Invalid credentials')
8594
st.error(f"Login Failed: {_format_error(detail)}")

0 commit comments

Comments
 (0)