Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/env/dev.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SQL_PORT=5432
# SQL_SSL_MODE=require

LOGIN_REDIRECT_URL=
CORS_ALLOWED_ORIGINS=http://localhost:3000
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
PINECONE_API_KEY=
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/components/ProtectedRoute/AdminRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ReactNode, useEffect } from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../../services/actions/types';
import { AppDispatch, checkAuthenticated } from '../../services/actions/auth';
import Spinner from '../LoadingSpinner/LoadingSpinner';

interface AdminRouteProps {
children: ReactNode;
}

const AdminRoute = ({ children }: AdminRouteProps) => {
const location = useLocation();
const dispatch = useDispatch<AppDispatch>();
const { isAuthenticated, isSuperuser } = useSelector((state: RootState) => state.auth);

useEffect(() => {
if (isAuthenticated === null) {
dispatch(checkAuthenticated());
}
}, [dispatch, isAuthenticated]);

if (isAuthenticated === null) {
return <Spinner />;
}

if (!isAuthenticated) {
return <Navigate to="/login" replace state={{ from: location }} />;
}

if (!isSuperuser) {
return <Navigate to="/" replace />;
}

return children;
};

export default AdminRoute;
13 changes: 7 additions & 6 deletions frontend/src/routes/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ListofFiles from "../pages/Files/ListOfFiles.tsx";
import RulesManager from "../pages/RulesManager/RulesManager.tsx";
import ManageMeds from "../pages/ManageMeds/ManageMeds.tsx";
import ProtectedRoute from "../components/ProtectedRoute/ProtectedRoute.tsx";
import AdminRoute from "../components/ProtectedRoute/AdminRoute.tsx";

const routes = [
{
Expand All @@ -28,17 +29,17 @@ const routes = [
},
{
path: "listoffiles",
element: <ProtectedRoute><ListofFiles /></ProtectedRoute>,
element: <AdminRoute><ListofFiles /></AdminRoute>,
errorElement: <RouteError />,
},
{
path: "rulesmanager",
element: <ProtectedRoute><RulesManager /></ProtectedRoute>,
element: <AdminRoute><RulesManager /></AdminRoute>,
errorElement: <RouteError />,
},
{
path: "uploadfile",
element: <ProtectedRoute><UploadFile /></ProtectedRoute>,
element: <AdminRoute><UploadFile /></AdminRoute>,
},
{
path: "drugSummary",
Expand Down Expand Up @@ -86,19 +87,19 @@ const routes = [
},
{
path: "adminportal",
element: <ProtectedRoute><AdminPortal /></ProtectedRoute>,
element: <AdminRoute><AdminPortal /></AdminRoute>,
},
{
path: "Settings",
element: <ProtectedRoute><Settings /></ProtectedRoute>,
element: <AdminRoute><Settings /></AdminRoute>,
},
{
path: "medications",
element: <ListMeds />,
},
{
path: "managemeds",
element: <ProtectedRoute><ManageMeds /></ProtectedRoute>,
element: <AdminRoute><ManageMeds /></AdminRoute>,
},
];

Expand Down
7 changes: 5 additions & 2 deletions frontend/src/services/reducers/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,15 @@ const initialState: StateType = {

export default function authReducer(state = initialState, action: ActionType): StateType {
switch(action.type) {
case AUTHENTICATED_SUCCESS:
case AUTHENTICATED_SUCCESS: {
const token = localStorage.getItem('access');
const decoded: TokenClaims = token ? jwtDecode(token) : { is_superuser: false };
return {
...state,
isAuthenticated: true,
isSuperuser: true
isSuperuser: decoded.is_superuser
}
}
case LOGIN_SUCCESS:
case GOOGLE_AUTH_SUCCESS:
case FACEBOOK_AUTH_SUCCESS:{
Expand Down
6 changes: 6 additions & 0 deletions server/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from rest_framework.permissions import BasePermission


class IsSuperUser(BasePermission):
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated and request.user.is_superuser)
4 changes: 2 additions & 2 deletions server/api/views/ai_settings/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from api.permissions import IsSuperUser
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from .models import AI_Settings
Expand All @@ -9,7 +9,7 @@

@extend_schema(request=AISettingsSerializer, responses={200: AISettingsSerializer(many=True), 201: AISettingsSerializer})
@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
@permission_classes([IsSuperUser])
def settings_view(request):
if request.method == 'GET':
settings = AI_Settings.objects.all()
Expand Down
3 changes: 3 additions & 0 deletions server/api/views/listMeds/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rest_framework import status, serializers as drf_serializers
from rest_framework.permissions import AllowAny
from api.permissions import IsSuperUser
from rest_framework.response import Response
from rest_framework.views import APIView
from drf_spectacular.utils import extend_schema, inline_serializer
Expand Down Expand Up @@ -127,6 +128,7 @@ class AddMedication(APIView):
"""
API endpoint to add a medication to the database with its risks and benefits.
"""
permission_classes = [IsSuperUser]
serializer_class = MedicationSerializer

def post(self, request):
Expand Down Expand Up @@ -158,6 +160,7 @@ class DeleteMedication(APIView):
"""
API endpoint to delete medication if medication in database.
"""
permission_classes = [IsSuperUser]

@extend_schema(
request=inline_serializer(name='DeleteMedicationRequest', fields={
Expand Down
4 changes: 2 additions & 2 deletions server/api/views/medRules/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status, serializers as drf_serializers
from api.permissions import IsSuperUser
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from drf_spectacular.utils import extend_schema, inline_serializer
Expand All @@ -13,7 +13,7 @@

@method_decorator(csrf_exempt, name='dispatch')
class MedRules(APIView):
permission_classes = [IsAuthenticated]
permission_classes = [IsSuperUser]
serializer_class = MedRuleSerializer

def get(self, request, format=None):
Expand Down
6 changes: 3 additions & 3 deletions server/api/views/text_extraction/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from api.permissions import IsSuperUser
from rest_framework.response import Response
from rest_framework import status
from django.utils.decorators import method_decorator
Expand Down Expand Up @@ -97,7 +97,7 @@ def anthropic_citations(client: anthropic.Client, user_prompt: str, content_chun
@method_decorator(csrf_exempt, name='dispatch')
class RuleExtractionAPIView(APIView):

permission_classes = [IsAuthenticated]
permission_classes = [IsSuperUser]

@extend_schema(
parameters=[
Expand Down Expand Up @@ -155,7 +155,7 @@ def openai_extraction(content_chunks, user_prompt):

@method_decorator(csrf_exempt, name='dispatch')
class RuleExtractionAPIOpenAIView(APIView):
permission_classes = [IsAuthenticated]
permission_classes = [IsSuperUser]

@extend_schema(
parameters=[
Expand Down
7 changes: 4 additions & 3 deletions server/api/views/uploadFile/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from rest_framework.views import APIView
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.permissions import AllowAny
from api.permissions import IsSuperUser
from rest_framework.response import Response
from rest_framework import status, serializers as drf_serializers
from rest_framework.generics import UpdateAPIView
Expand All @@ -24,7 +25,7 @@ class UploadFileView(APIView):
def get_permissions(self):
if self.request.method == 'GET':
return [AllowAny()] # Public access
return [IsAuthenticated()] # Auth required for other methods
return [IsSuperUser()] # Superuser required for write methods

def get(self, request, format=None):
print("UploadFileView, get list")
Expand Down Expand Up @@ -217,7 +218,7 @@ def get(self, request, guid, format=None):


class EditFileMetadataView(UpdateAPIView):
permission_classes = [IsAuthenticated]
permission_classes = [IsSuperUser]
serializer_class = UploadFileSerializer
lookup_field = 'guid'

Expand Down
2 changes: 1 addition & 1 deletion server/balancer_backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@

ROOT_URLCONF = "balancer_backend.urls"

CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOWED_ORIGINS = os.environ.get("CORS_ALLOWED_ORIGINS", "http://localhost:3000").split(",")

TEMPLATES = [
{
Expand Down
Loading