Implement RBAC in api-gw

This commit is contained in:
Santiago Lo Coco 2023-11-12 11:02:56 -03:00
parent 7aa05f51ff
commit 51f0c7b168
8 changed files with 72 additions and 29 deletions

View File

@ -9,6 +9,7 @@ from src.api.models.users import User
auth_namespace = Namespace("auth")
auth_user_model = User.get_api_auth_user_model(auth_namespace)
auth_small_model = User.get_api_auth_small_model(auth_namespace)
auth_full_user_model = User.get_api_auth_full_user_model(auth_namespace)
auth_login_model = User.get_api_auth_login_model(auth_namespace)
auth_refresh_model = User.get_api_auth_refresh_model(auth_namespace)
@ -97,7 +98,7 @@ class Refresh(Resource):
class Status(Resource):
@auth_namespace.marshal_with(auth_user_model)
@auth_namespace.marshal_with(auth_small_model)
@auth_namespace.response(200, "Success")
@auth_namespace.response(401, "Invalid token")
@auth_namespace.expect(parser)

View File

@ -85,6 +85,16 @@ class User(db.Model):
},
)
@classmethod
def get_api_auth_small_model(cls, namespace):
return namespace.model(
"User",
{
"id": fields.Integer(required=True),
"airline": fields.Boolean(readOnly=True),
},
)
@classmethod
def get_api_auth_full_user_model(cls, namespace):
return namespace.clone(

View File

@ -31,12 +31,10 @@ export const createUser = (
return instance.post("users", credentials);
};
export const fetchUsers = (): Promise<User[]> => {
return instance.get("users");
};
export const fetchUserById = (id: number): Promise<User> => {
return instance.get("users/" + id);
export const fetchUserById = (id: number, token: string): Promise<User & { message?: string }> => {
return instance.get("users/" + id, {
headers: { Authorization: `Bearer ${token}` },
});
};
export const logIn = (

View File

@ -48,7 +48,7 @@ export function AuthProvider({
}
tokenStatus(existingToken)
.then((res) => fetchUserById(res.id)
.then((res) => fetchUserById(res.id, existingToken)
.then((res) => setUser(res))
.catch((_error) => { })
.finally(() => setLoadingInitial(false))
@ -70,7 +70,7 @@ export function AuthProvider({
localStorage.setItem("token", x.access_token);
const airline = (jwt_decode(x.access_token) as TokenData).airline;
setIsAirline(airline)
const user = fetchUserById(x.user_id as number)
const user = fetchUserById(x.user_id as number, x.access_token)
.then(y => {
setUser(y);
navigate("/home")

View File

@ -5,7 +5,7 @@ from fastapi import APIRouter, Header, HTTPException, Request
from src.api.config import API_AUTH
from src.api.schemas.auth import RefreshToken, Token
from src.api.schemas.user import UserLogin, UserMin, UserRegister
from src.api.schemas.user import UserLogin, UserMin, UserRegister, UserStatus
router = APIRouter()
@ -46,7 +46,7 @@ async def refresh(token: RefreshToken, req: Request):
return response
@router.get("/status", response_model=UserMin)
@router.get("/status", response_model=UserStatus)
async def status(req: Request, authorization: Annotated[str | None, Header()] = None):
request_id = req.state.request_id
header = {
@ -57,3 +57,27 @@ async def status(req: Request, authorization: Annotated[str | None, Header()] =
if status < 200 or status > 204:
raise HTTPException(status_code=status, detail=response)
return response
async def checkAuth(
req: Request,
authorization: Annotated[str | None, Header()] = None,
isAirline=False,
userId=None,
):
response = await status(req, authorization)
if isAirline:
if response["airline"]:
return response["id"]
else:
raise HTTPException(
status_code=403, detail="You don't have the required permissions."
)
elif userId:
if response["id"] != int(userId):
raise HTTPException(
status_code=403, detail="You don't have the required permissions."
)
return None
else:
return response["id"]

View File

@ -4,7 +4,7 @@ from asyncreq import request
from fastapi import APIRouter, Header, HTTPException, Request
from src.api.config import API_FLIGHTS
from src.api.routes.auth import status as checkAuth
from src.api.routes.auth import checkAuth
from src.api.schemas.flight import Flight, FlightCreate, FlightUpdate
router = APIRouter()
@ -29,7 +29,7 @@ async def create_flight(
req: Request,
authorization: Annotated[str | None, Header()] = None,
):
auth = await checkAuth(req, authorization)
auth = await checkAuth(req, authorization, isAirline=True)
flight_data = flight.model_dump()
flight_data["user_id"] = auth["id"]
request_id = req.state.request_id
@ -49,7 +49,7 @@ async def update_flight(
req: Request,
authorization: Annotated[str | None, Header()] = None,
):
auth = await checkAuth(req, authorization)
auth = await checkAuth(req, authorization, isAirline=True)
update = flight_update.model_dump()
update["user_id"] = auth["id"]
request_id = req.state.request_id

View File

@ -1,22 +1,15 @@
from typing import Annotated
from asyncreq import request
from fastapi import APIRouter, HTTPException, Request
from fastapi import APIRouter, Header, HTTPException, Request
from src.api.config import API_USERS
from src.api.routes.auth import checkAuth
from src.api.schemas.user import User, UserRegister
router = APIRouter()
@router.get("", response_model=list[User])
async def get_users(req: Request):
request_id = req.state.request_id
header = {"x-api-request-id": request_id}
(response, status, _) = await request(f"{API_USERS}", "GET", headers=header)
if status < 200 or status > 204:
raise HTTPException(status_code=status, detail=response)
return response
@router.post("", response_model=User)
async def create_users(user: UserRegister, req: Request):
request_id = req.state.request_id
@ -30,7 +23,10 @@ async def create_users(user: UserRegister, req: Request):
@router.get("/{id}", response_model=User)
async def get_user(id: str, req: Request):
async def get_user(
id: str, req: Request, authorization: Annotated[str | None, Header()] = None
):
await checkAuth(req, authorization, userId=id)
request_id = req.state.request_id
header = {"x-api-request-id": request_id}
(response, status, _) = await request(f"{API_USERS}/{id}", "GET", headers=header)
@ -40,7 +36,13 @@ async def get_user(id: str, req: Request):
@router.put("/{id}", response_model=User)
async def update_user(user: UserRegister, req: Request):
async def update_user(
id: str,
user: UserRegister,
req: Request,
authorization: Annotated[str | None, Header()] = None,
):
await checkAuth(req, authorization, userId=id)
request_id = req.state.request_id
header = {"x-api-request-id": request_id}
(response, status, _) = await request(
@ -51,8 +53,11 @@ async def update_user(user: UserRegister, req: Request):
return response
@router.delete("/{id}", response_model=User)
async def delete_user(req: Request):
@router.delete("/{id}")
async def delete_user(
id: str, req: Request, authorization: Annotated[str | None, Header()] = None
):
await checkAuth(req, authorization, userId=id)
request_id = req.state.request_id
header = {"x-api-request-id": request_id}
(response, status, _) = await request(f"{API_USERS}/{id}", "DELETE", headers=header)

View File

@ -15,6 +15,11 @@ class UserMin(BaseModel):
email: str
class UserStatus(BaseModel):
id: int
airline: bool
class UserRegister(BaseModel):
username: str
email: str