From 524b4763b7860d18062e4727e923080679b85d81 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Tue, 10 Oct 2023 22:13:47 +0200 Subject: [PATCH 01/10] add users routers --- app/__init__.py | 0 app/main.py | 0 app/routers/users.py | 7 +++++++ requierements.txt | 2 ++ 4 files changed, 9 insertions(+) create mode 100644 app/__init__.py create mode 100644 app/main.py create mode 100644 app/routers/users.py create mode 100644 requierements.txt diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..e69de29 diff --git a/app/routers/users.py b/app/routers/users.py new file mode 100644 index 0000000..e3352a8 --- /dev/null +++ b/app/routers/users.py @@ -0,0 +1,7 @@ +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/users/", tags=["users"]) +async def read_users(): + return [{"username":"toto"}] \ No newline at end of file diff --git a/requierements.txt b/requierements.txt new file mode 100644 index 0000000..8e0578a --- /dev/null +++ b/requierements.txt @@ -0,0 +1,2 @@ +fastapi +uvicorn[standard] \ No newline at end of file From e28520a5fb00ac1035d621cece7d360ff87be9f0 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Tue, 10 Oct 2023 22:39:23 +0200 Subject: [PATCH 02/10] add models --- app/main.py | 11 +++++++++++ app/models/__init__.py | 0 app/models/users.py | 12 ++++++++++++ app/routers/__init__.py | 0 requierements.txt | 3 ++- 5 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 app/models/__init__.py create mode 100644 app/models/users.py create mode 100644 app/routers/__init__.py diff --git a/app/main.py b/app/main.py index e69de29..5dfe2c7 100644 --- a/app/main.py +++ b/app/main.py @@ -0,0 +1,11 @@ +from fastapi import FastAPI + +from .routers import users + +app = FastAPI() + +app.include_router(users.router) + +@app.get("/") +async def root(): + return {"message": "Hello World !"} \ No newline at end of file diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/models/users.py b/app/models/users.py new file mode 100644 index 0000000..b3da0b6 --- /dev/null +++ b/app/models/users.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel +from pydantic_mongo import AbstractRepository, ObjectIdField + + +class Users(BaseModel): + id: ObjectIdField = None + username: str + + +class UsersRepository(AbstractRepository[Users]): + class Meta: + collection_name = users \ No newline at end of file diff --git a/app/routers/__init__.py b/app/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/requierements.txt b/requierements.txt index 8e0578a..13a6b2c 100644 --- a/requierements.txt +++ b/requierements.txt @@ -1,2 +1,3 @@ fastapi -uvicorn[standard] \ No newline at end of file +uvicorn[standard] +pydantic-mongo \ No newline at end of file From 4a3827f7c072b50a84c5656c2cb56cea8fcf589b Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Wed, 11 Oct 2023 23:45:12 +0200 Subject: [PATCH 03/10] add dependencies --- app/dependencies.py | 74 +++++++++++++++++++++++++++++++++++++++++++ app/main.py | 1 + app/models/users.py | 13 ++++---- app/routers/events.py | 0 app/routers/users.py | 10 ++++++ 5 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 app/dependencies.py create mode 100644 app/routers/events.py diff --git a/app/dependencies.py b/app/dependencies.py new file mode 100644 index 0000000..c34a1f7 --- /dev/null +++ b/app/dependencies.py @@ -0,0 +1,74 @@ +from datetime import datetime, timedelta +from typing import Annotated + +from fastapi import Depends, HTTPException, status +from fastapi.security import OAuth2PasswordBearer +from jose import JWTError, jwt +from passlib.context import CryptContext + + +fake_users = [ + # password foo + {'id': 1, 'username': 'admin', 'password': '$2b$12$N.i74Kle18n5Toxhas.rVOjZreVC2WM34fCidNDyhSNgxVlbKwX7i', + 'permissions': ['items:read', 'items:write', 'users:read', 'users:write'] + }, + # password bar + {'id': 2, 'username': 'client', 'password': '$2b$12$KUgpw1m0LF/s9NS1ZB5rRO2cA5D13MqRm56ab7ik2ixftXW/aqEyq', + 'permissions': ['items:read']} +] + +SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_MINUTES = 30 + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + +def verify_password(plain_password, hashed_password): + return pwd_context.verify(plain_password, hashed_password) + +def get_password_hash(password): + return pwd_context.hash(password) + +def get_user(db, username: str): + for user in db: + if username == user.username: + return UserInDB(**user) + +def authenticate_user(fake_db, username: str, password: str): + user = get_user(fake_db, username) + if not user: + return False + if not verify_password(password, user.password): + return False + return user + +def create_access_token(data: dict, expires_delta: timedelta | None = None): + to_encode = data.copy() + if expires_delta: + expire = datetime.utcnow() + expires_delta + else: + expire = datetime.utcnow() + timedelta(minutes=15) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + return encoded_jwt + +async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): + credentials_exception = HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Could not validate credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + username: str = payload.get("sub") + if username is None: + raise credentials_exception + token_data = TokenData(username=username) + except JWTError: + raise credentials_exception + user = get_user(fake_users, username=token_data.username) + if user is None: + raise credentials_exception + return user diff --git a/app/main.py b/app/main.py index 5dfe2c7..a50b2c5 100644 --- a/app/main.py +++ b/app/main.py @@ -2,6 +2,7 @@ from fastapi import FastAPI from .routers import users + app = FastAPI() app.include_router(users.router) diff --git a/app/models/users.py b/app/models/users.py index b3da0b6..e1b9b4d 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -1,12 +1,11 @@ from pydantic import BaseModel -from pydantic_mongo import AbstractRepository, ObjectIdField -class Users(BaseModel): - id: ObjectIdField = None +class User(BaseModel): + id: int username: str + password: str + permissions: list[str] = [] - -class UsersRepository(AbstractRepository[Users]): - class Meta: - collection_name = users \ No newline at end of file +class UserInDB(User): + password: str \ No newline at end of file diff --git a/app/routers/events.py b/app/routers/events.py new file mode 100644 index 0000000..e69de29 diff --git a/app/routers/users.py b/app/routers/users.py index e3352a8..2dabaa1 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,5 +1,15 @@ from fastapi import APIRouter +fake_users = [ + # password foo + {'id': 1, 'username': 'admin', 'password': '$2b$12$N.i74Kle18n5Toxhas.rVOjZreVC2WM34fCidNDyhSNgxVlbKwX7i', + 'permissions': ['items:read', 'items:write', 'users:read', 'users:write'] + }, + # password bar + {'id': 2, 'username': 'client', 'password': '$2b$12$KUgpw1m0LF/s9NS1ZB5rRO2cA5D13MqRm56ab7ik2ixftXW/aqEyq', + 'permissions': ['items:read']} +] + router = APIRouter() @router.get("/users/", tags=["users"]) From bf07aa3df3a0c0fdd9740118d41109a0fbed0765 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 00:04:01 +0200 Subject: [PATCH 04/10] add ignore git --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file From 4d38ecc08c41bc4dfd3f9860c4bec2f89d1e36cf Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 00:04:18 +0200 Subject: [PATCH 05/10] add dependances --- app/main.py | 3 ++- app/models/token.py | 9 +++++++++ app/models/users.py | 2 +- app/routers/token.py | 25 +++++++++++++++++++++++++ app/routers/users.py | 10 +--------- requierements.txt | 5 ++++- 6 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 app/models/token.py create mode 100644 app/routers/token.py diff --git a/app/main.py b/app/main.py index a50b2c5..28a1294 100644 --- a/app/main.py +++ b/app/main.py @@ -1,11 +1,12 @@ from fastapi import FastAPI -from .routers import users +from .routers import users, token app = FastAPI() app.include_router(users.router) +app.include_router(token.router) @app.get("/") async def root(): diff --git a/app/models/token.py b/app/models/token.py new file mode 100644 index 0000000..c3c0548 --- /dev/null +++ b/app/models/token.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel + +class Token(BaseModel): + access_token: str + token_type: str + + +class TokenData(BaseModel): + username: str | None = None \ No newline at end of file diff --git a/app/models/users.py b/app/models/users.py index e1b9b4d..1888557 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -5,7 +5,7 @@ class User(BaseModel): id: int username: str password: str - permissions: list[str] = [] + roles: str class UserInDB(User): password: str \ No newline at end of file diff --git a/app/routers/token.py b/app/routers/token.py new file mode 100644 index 0000000..1995ad9 --- /dev/null +++ b/app/routers/token.py @@ -0,0 +1,25 @@ +from typing import Annotated +from fastapi import Depends, FastAPI, HTTPException, status, APIRouter +from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm +from ..dependencies import fake_users, authenticate_user +from ..models import token + +router = APIRouter() + + +@router.post("/token", response_model=token.Token, tags=["token"]) +async def login_for_access_token( + form_data: Annotated[OAuth2PasswordRequestForm, Depends()] +): + user = authenticate_user(fake_users, form_data.username, form_data.password) + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Bearer"}, + ) + access_token_expires = timedelta(minutes=dependencies.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = create_access_token( + data={"sub": user.username}, expires_delta=access_token_expires + ) + return {"access_token": access_token, "token_type": "bearer"} \ No newline at end of file diff --git a/app/routers/users.py b/app/routers/users.py index 2dabaa1..079de7b 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,14 +1,6 @@ from fastapi import APIRouter +from ..models import users -fake_users = [ - # password foo - {'id': 1, 'username': 'admin', 'password': '$2b$12$N.i74Kle18n5Toxhas.rVOjZreVC2WM34fCidNDyhSNgxVlbKwX7i', - 'permissions': ['items:read', 'items:write', 'users:read', 'users:write'] - }, - # password bar - {'id': 2, 'username': 'client', 'password': '$2b$12$KUgpw1m0LF/s9NS1ZB5rRO2cA5D13MqRm56ab7ik2ixftXW/aqEyq', - 'permissions': ['items:read']} -] router = APIRouter() diff --git a/requierements.txt b/requierements.txt index 13a6b2c..5bd6d38 100644 --- a/requierements.txt +++ b/requierements.txt @@ -1,3 +1,6 @@ fastapi uvicorn[standard] -pydantic-mongo \ No newline at end of file +pydantic-mongo +python-jose[cryptography] +passlib[bcrypt] +python-multipart \ No newline at end of file From 6efe9fb5a740298f5a87a544254946a10ebac129 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 00:14:50 +0200 Subject: [PATCH 06/10] fix users --- app/dependencies.py | 11 ++++++++++- app/routers/users.py | 10 ++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/dependencies.py b/app/dependencies.py index c34a1f7..20a9a91 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -6,6 +6,8 @@ from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from passlib.context import CryptContext +from .models import users + fake_users = [ # password foo @@ -34,7 +36,7 @@ def get_password_hash(password): def get_user(db, username: str): for user in db: if username == user.username: - return UserInDB(**user) + return users.UserInDB(**user) def authenticate_user(fake_db, username: str, password: str): user = get_user(fake_db, username) @@ -72,3 +74,10 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): if user is None: raise credentials_exception return user + +async def get_current_active_user( + current_user: Annotated[users.User, Depends(get_current_user)] +): + if current_user.disabled: + raise HTTPException(status_code=400, detail="Inactive user") + return current_user \ No newline at end of file diff --git a/app/routers/users.py b/app/routers/users.py index 079de7b..c6a7e31 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,9 +1,15 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Depends from ..models import users +from ..dependencies import get_current_active_user +from typing import Annotated router = APIRouter() @router.get("/users/", tags=["users"]) async def read_users(): - return [{"username":"toto"}] \ No newline at end of file + return [{"username":"toto"}] + +@router.get("/users/me", response_model=users.User) +async def read_users_me(current_user: Annotated[users.User, Depends(get_current_active_user)]): + return [{"item_id": "Foo", "owner": current_user.username}] \ No newline at end of file From 08758aa12fb956f637a307c99cd9ff0c17e6d70c Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 23:01:51 +0200 Subject: [PATCH 07/10] access token ok --- app/dependencies.py | 6 +++--- app/routers/token.py | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/dependencies.py b/app/dependencies.py index 20a9a91..1b7328a 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -12,11 +12,11 @@ from .models import users fake_users = [ # password foo {'id': 1, 'username': 'admin', 'password': '$2b$12$N.i74Kle18n5Toxhas.rVOjZreVC2WM34fCidNDyhSNgxVlbKwX7i', - 'permissions': ['items:read', 'items:write', 'users:read', 'users:write'] + 'roles': 'Admin' }, # password bar {'id': 2, 'username': 'client', 'password': '$2b$12$KUgpw1m0LF/s9NS1ZB5rRO2cA5D13MqRm56ab7ik2ixftXW/aqEyq', - 'permissions': ['items:read']} + 'roles':'User'} ] SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" @@ -35,7 +35,7 @@ def get_password_hash(password): def get_user(db, username: str): for user in db: - if username == user.username: + if username == user['username']: return users.UserInDB(**user) def authenticate_user(fake_db, username: str, password: str): diff --git a/app/routers/token.py b/app/routers/token.py index 1995ad9..9a889d9 100644 --- a/app/routers/token.py +++ b/app/routers/token.py @@ -1,7 +1,9 @@ +from datetime import datetime, timedelta + from typing import Annotated from fastapi import Depends, FastAPI, HTTPException, status, APIRouter from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm -from ..dependencies import fake_users, authenticate_user +from ..dependencies import fake_users, authenticate_user, ACCESS_TOKEN_EXPIRE_MINUTES, create_access_token from ..models import token router = APIRouter() @@ -18,7 +20,7 @@ async def login_for_access_token( detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) - access_token_expires = timedelta(minutes=dependencies.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) From e8d2670024947c2ab90a893ab2d296a50347dcc0 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 23:15:26 +0200 Subject: [PATCH 08/10] get token fonctionnel --- app/dependencies.py | 12 ++++++------ app/models/users.py | 1 + app/routers/users.py | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/dependencies.py b/app/dependencies.py index 1b7328a..5692bb6 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -6,17 +6,17 @@ from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from passlib.context import CryptContext -from .models import users +from .models import users, token fake_users = [ # password foo {'id': 1, 'username': 'admin', 'password': '$2b$12$N.i74Kle18n5Toxhas.rVOjZreVC2WM34fCidNDyhSNgxVlbKwX7i', - 'roles': 'Admin' + 'roles': 'Admin', 'disabled': False }, # password bar {'id': 2, 'username': 'client', 'password': '$2b$12$KUgpw1m0LF/s9NS1ZB5rRO2cA5D13MqRm56ab7ik2ixftXW/aqEyq', - 'roles':'User'} + 'roles':'User', 'disabled':False} ] SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" @@ -56,18 +56,18 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None): encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt -async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): +async def get_current_user(token_str: Annotated[str, Depends(oauth2_scheme)]): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: - payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + payload = jwt.decode(token_str, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception - token_data = TokenData(username=username) + token_data = token.TokenData(username=username) except JWTError: raise credentials_exception user = get_user(fake_users, username=token_data.username) diff --git a/app/models/users.py b/app/models/users.py index 1888557..de11fcd 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -6,6 +6,7 @@ class User(BaseModel): username: str password: str roles: str + disabled: bool class UserInDB(User): password: str \ No newline at end of file diff --git a/app/routers/users.py b/app/routers/users.py index c6a7e31..3dc18db 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -12,4 +12,4 @@ async def read_users(): @router.get("/users/me", response_model=users.User) async def read_users_me(current_user: Annotated[users.User, Depends(get_current_active_user)]): - return [{"item_id": "Foo", "owner": current_user.username}] \ No newline at end of file + return current_user \ No newline at end of file From f5da8ed3c4773fea4a71018199ccc515db7ae092 Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 23:35:45 +0200 Subject: [PATCH 09/10] move users_active to folder dependancies --- app/dependencies/__init__.py | 0 .../users_active.py} | 2 +- app/routers/token.py | 8 ++++---- app/routers/users.py | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 app/dependencies/__init__.py rename app/{dependencies.py => dependencies/users_active.py} (98%) diff --git a/app/dependencies/__init__.py b/app/dependencies/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/dependencies.py b/app/dependencies/users_active.py similarity index 98% rename from app/dependencies.py rename to app/dependencies/users_active.py index 5692bb6..9ca2b78 100644 --- a/app/dependencies.py +++ b/app/dependencies/users_active.py @@ -6,7 +6,7 @@ from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from passlib.context import CryptContext -from .models import users, token +from ..models import users, token fake_users = [ diff --git a/app/routers/token.py b/app/routers/token.py index 9a889d9..4d99f28 100644 --- a/app/routers/token.py +++ b/app/routers/token.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta from typing import Annotated from fastapi import Depends, FastAPI, HTTPException, status, APIRouter from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm -from ..dependencies import fake_users, authenticate_user, ACCESS_TOKEN_EXPIRE_MINUTES, create_access_token +from ..dependencies import users_active from ..models import token router = APIRouter() @@ -13,15 +13,15 @@ router = APIRouter() async def login_for_access_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()] ): - user = authenticate_user(fake_users, form_data.username, form_data.password) + user = users_active.authenticate_user(users_active.fake_users, form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) - access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token( + access_token_expires = timedelta(minutes=users_active.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = users_active.create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} \ No newline at end of file diff --git a/app/routers/users.py b/app/routers/users.py index 3dc18db..250013b 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,15 +1,15 @@ from fastapi import APIRouter, Depends +from ..dependencies import users_active from ..models import users -from ..dependencies import get_current_active_user from typing import Annotated router = APIRouter() -@router.get("/users/", tags=["users"]) -async def read_users(): - return [{"username":"toto"}] +@router.get("/users/", tags=["users"], response_model=list[users.User]) +async def read_users(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)]): + return users_active.fake_users -@router.get("/users/me", response_model=users.User) -async def read_users_me(current_user: Annotated[users.User, Depends(get_current_active_user)]): +@router.get("/users/me",tags=["users"], response_model=users.User) +async def read_users_me(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)]): return current_user \ No newline at end of file From c09eb45ac291c4ea9f38512b350716f7ef81ab3a Mon Sep 17 00:00:00 2001 From: Valentin CZERYBA Date: Thu, 12 Oct 2023 23:54:50 +0200 Subject: [PATCH 10/10] permission checker --- app/dependencies/permissions_checker.py | 18 ++++++++++++++++++ app/routers/users.py | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 app/dependencies/permissions_checker.py diff --git a/app/dependencies/permissions_checker.py b/app/dependencies/permissions_checker.py new file mode 100644 index 0000000..896679f --- /dev/null +++ b/app/dependencies/permissions_checker.py @@ -0,0 +1,18 @@ +from ..dependencies import users_active +from fastapi import Depends, HTTPException, status +from ..models import users + + +class PermissionChecker: + + def __init__(self, roles: list[str]) -> None: + self.roles = roles + + def __call__(self, user: users.User = Depends(users_active.get_current_active_user)) -> bool: + for role in self.roles: + if role == user.roles: + return True + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail='Roles unauthorized' + ) diff --git a/app/routers/users.py b/app/routers/users.py index 250013b..0ec1ccf 100644 --- a/app/routers/users.py +++ b/app/routers/users.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, Depends -from ..dependencies import users_active +from ..dependencies import users_active, permissions_checker from ..models import users from typing import Annotated @@ -7,9 +7,9 @@ from typing import Annotated router = APIRouter() @router.get("/users/", tags=["users"], response_model=list[users.User]) -async def read_users(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)]): +async def read_users(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin"]))]): return users_active.fake_users @router.get("/users/me",tags=["users"], response_model=users.User) -async def read_users_me(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)]): +async def read_users_me(current_user: Annotated[users.User, Depends(users_active.get_current_active_user)], authorize: Annotated[bool, Depends(permissions_checker.PermissionChecker(roles=["Admin", "User"]))]): return current_user \ No newline at end of file