structure auth
This commit is contained in:
BIN
server/__pycache__/config.cpython-311.pyc
Normal file
BIN
server/__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
fastapi==0.128.6
|
fastapi==0.128.6
|
||||||
sqlmodel==0.0.32
|
sqlmodel==0.0.32
|
||||||
|
python-dotenv==1.2.1
|
||||||
openai==2.21.0
|
openai==2.21.0
|
||||||
spacy==3.8.11
|
spacy==3.8.11
|
||||||
PyJWT>=2.11.0
|
PyJWT>=2.11.0
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ LANGUAGE_MODEL_API="http://localhost:8080/v1"
|
|||||||
LANGUAGE_MODEL_NAME="SmolLM3-Q4_K_M.gguf"
|
LANGUAGE_MODEL_NAME="SmolLM3-Q4_K_M.gguf"
|
||||||
ORIGIN="http://localhost:5173"
|
ORIGIN="http://localhost:5173"
|
||||||
SECRET_KEY="xxxx" #generate secure random secret key: openssl rand -hex 32
|
SECRET_KEY="xxxx" #generate secure random secret key: openssl rand -hex 32
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES=10080
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,9 +2,9 @@ from fastapi import APIRouter
|
|||||||
|
|
||||||
from .knowledges import router as knowledge_router
|
from .knowledges import router as knowledge_router
|
||||||
from .metrics import router as metric_router
|
from .metrics import router as metric_router
|
||||||
from .users import router as user_router
|
from .auth import router as auth_router
|
||||||
|
|
||||||
router = APIRouter(prefix="/v1")
|
router = APIRouter(prefix="/v1")
|
||||||
router.include_router(knowledge_router)
|
router.include_router(knowledge_router)
|
||||||
router.include_router(metric_router)
|
router.include_router(metric_router)
|
||||||
router.include_router(user_router)
|
router.include_router(auth_router)
|
||||||
Binary file not shown.
BIN
server/src/app/api/v1/__pycache__/auth.cpython-311.pyc
Normal file
BIN
server/src/app/api/v1/__pycache__/auth.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -2,11 +2,13 @@ from typing import Annotated
|
|||||||
|
|
||||||
from fastapi import Depends, APIRouter, HTTPException, status
|
from fastapi import Depends, APIRouter, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from src.app.models.user import User
|
from src.app.models.user import User
|
||||||
from src.app.data.user import create_user
|
from src.app.data.user import create_user
|
||||||
from src.app.services.auth import get_current_user, authenticate_user, create_access_token, hash_password, Token
|
|
||||||
|
from src.app.auth.dependancies import get_current_user, authenticate_user
|
||||||
|
from src.app.auth.security import hash_password, create_access_token
|
||||||
|
from src.app.auth.schemas import Token
|
||||||
|
|
||||||
router = APIRouter(tags=["users"])
|
router = APIRouter(tags=["users"])
|
||||||
|
|
||||||
BIN
server/src/app/auth/__pycache__/auth.cpython-311.pyc
Normal file
BIN
server/src/app/auth/__pycache__/auth.cpython-311.pyc
Normal file
Binary file not shown.
BIN
server/src/app/auth/__pycache__/dependancies.cpython-311.pyc
Normal file
BIN
server/src/app/auth/__pycache__/dependancies.cpython-311.pyc
Normal file
Binary file not shown.
BIN
server/src/app/auth/__pycache__/schemas.cpython-311.pyc
Normal file
BIN
server/src/app/auth/__pycache__/schemas.cpython-311.pyc
Normal file
Binary file not shown.
BIN
server/src/app/auth/__pycache__/security.cpython-311.pyc
Normal file
BIN
server/src/app/auth/__pycache__/security.cpython-311.pyc
Normal file
Binary file not shown.
@@ -1,35 +1,21 @@
|
|||||||
from src.app.config import settings
|
from src.app.config import settings
|
||||||
from datetime import timedelta, datetime, timezone
|
|
||||||
from typing import Annotated
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
|
from typing import Annotated
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer
|
||||||
|
from argon2 import PasswordHasher
|
||||||
|
from argon2 import PasswordHasher
|
||||||
import jwt
|
import jwt
|
||||||
from jwt.exceptions import InvalidTokenError
|
from jwt.exceptions import InvalidTokenError
|
||||||
from argon2 import PasswordHasher
|
|
||||||
from argon2.exceptions import (
|
|
||||||
VerifyMismatchError,
|
|
||||||
VerificationError,
|
|
||||||
InvalidHashError,
|
|
||||||
)
|
|
||||||
|
|
||||||
from src.app.models.user import User
|
from src.app.models.user import User
|
||||||
from src.app.data.user import get_user
|
from src.app.data.user import get_user
|
||||||
|
from .schemas import TokenData
|
||||||
|
from .security import verify_password
|
||||||
|
|
||||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/token")
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/token")
|
||||||
password_hasher = PasswordHasher()
|
password_hasher = PasswordHasher()
|
||||||
|
|
||||||
algorithm = "HS256"
|
|
||||||
access_token_expire_minutes = 10080
|
|
||||||
|
|
||||||
class Token(BaseModel):
|
|
||||||
access_token: str
|
|
||||||
token_type: str
|
|
||||||
|
|
||||||
class TokenData(BaseModel):
|
|
||||||
username: str | None = None
|
|
||||||
|
|
||||||
def authenticate_user(username: str, password: str):
|
def authenticate_user(username: str, password: str):
|
||||||
user: User = get_user(username)
|
user: User = get_user(username)
|
||||||
if not user:
|
if not user:
|
||||||
@@ -40,23 +26,6 @@ def authenticate_user(username: str, password: str):
|
|||||||
return False
|
return False
|
||||||
return user
|
return user
|
||||||
|
|
||||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
||||||
isValidated: bool = False
|
|
||||||
try:
|
|
||||||
return password_hasher.verify(hashed_password, plain_password)
|
|
||||||
except (VerifyMismatchError, VerificationError, InvalidHashError):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def create_access_token(data: dict):
|
|
||||||
expire = datetime.now(timezone.utc) + timedelta(minutes=access_token_expire_minutes)
|
|
||||||
to_encode = data.copy()
|
|
||||||
to_encode.update({"exp": expire})
|
|
||||||
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=algorithm)
|
|
||||||
return encoded_jwt
|
|
||||||
|
|
||||||
async def hash_password(password: str) -> str:
|
|
||||||
return password_hasher.hash(password)
|
|
||||||
|
|
||||||
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> User:
|
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> User:
|
||||||
credentials_exception = HTTPException(
|
credentials_exception = HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
@@ -64,7 +33,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> Use
|
|||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithm=algorithm)
|
payload = jwt.decode(token, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||||
username = payload.get("sub")
|
username = payload.get("sub")
|
||||||
if username is None:
|
if username is None:
|
||||||
raise credentials_exception
|
raise credentials_exception
|
||||||
8
server/src/app/auth/schemas.py
Normal file
8
server/src/app/auth/schemas.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
class Token(BaseModel):
|
||||||
|
access_token: str
|
||||||
|
token_type: str
|
||||||
|
|
||||||
|
class TokenData(BaseModel):
|
||||||
|
username: str | None = None
|
||||||
33
server/src/app/auth/security.py
Normal file
33
server/src/app/auth/security.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from src.app.config import settings
|
||||||
|
from datetime import timedelta, datetime, timezone
|
||||||
|
from argon2 import PasswordHasher
|
||||||
|
from argon2.exceptions import (
|
||||||
|
VerifyMismatchError,
|
||||||
|
VerificationError,
|
||||||
|
InvalidHashError,
|
||||||
|
)
|
||||||
|
import jwt
|
||||||
|
from jwt.exceptions import InvalidTokenError
|
||||||
|
|
||||||
|
|
||||||
|
password_hasher = PasswordHasher()
|
||||||
|
|
||||||
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||||
|
try:
|
||||||
|
return password_hasher.verify(hashed_password, plain_password)
|
||||||
|
except (VerifyMismatchError, VerificationError, InvalidHashError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def hash_password(password: str) -> str:
|
||||||
|
return password_hasher.hash(password)
|
||||||
|
|
||||||
|
def create_access_token(data: dict):
|
||||||
|
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
to_encode = data.copy()
|
||||||
|
to_encode.update({"exp": expire})
|
||||||
|
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||||
|
return encoded_jwt
|
||||||
|
|
||||||
|
#def create_refresh_token(data: dict) -> str:
|
||||||
|
|
||||||
|
#def verify_token(token: str, token_type: str = "access") -> Optional[dict]:
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
# Database
|
# Database
|
||||||
DATABASE_URI: str
|
DATABASE_URI: str = Field("sqlite:///database.db", env='DATABASE_URI')
|
||||||
|
|
||||||
# Language model
|
# Language model
|
||||||
LANGUAGE_MODEL_API: str = "http://localhost:8080/v1"
|
LANGUAGE_MODEL_API: str = Field("http://localhost:8080/v1", env='LANGUAGE_MODEL_API')
|
||||||
LANGUAGE_MODEL_NAME: str = "SmolLM3-Q4_K_M.gguf"
|
LANGUAGE_MODEL_NAME: str = Field("SmolLM3-Q4_K_M.gguf", env='LANGUAGE_MODEL_NAME')
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
ORIGIN: str
|
ORIGIN: str = Field('http://localhost:5173', env='ORIGIN')
|
||||||
SECRET_KEY : str
|
SECRET_KEY : str = Field('random_string', env='SECRET_KEY')
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES: int
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||||
|
ALGORITHM: str = "HS256"
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
|
|||||||
0
server/src/app/data/__init__.py
Normal file
0
server/src/app/data/__init__.py
Normal file
BIN
server/src/app/data/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
server/src/app/data/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
@@ -6,6 +6,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||||||
|
|
||||||
from src.app.database import create_db_and_tables
|
from src.app.database import create_db_and_tables
|
||||||
|
|
||||||
|
# Import models in app
|
||||||
# TODO : best practice to manage models import
|
# TODO : best practice to manage models import
|
||||||
from src.app.models.question import Question
|
from src.app.models.question import Question
|
||||||
from src.app.models.knowledge import Knowledge
|
from src.app.models.knowledge import Knowledge
|
||||||
@@ -14,7 +15,7 @@ from src.app.models.user import User
|
|||||||
|
|
||||||
from .api import router
|
from .api import router
|
||||||
|
|
||||||
#Test
|
#Fake data
|
||||||
from src.app.faker_seed import faker
|
from src.app.faker_seed import faker
|
||||||
|
|
||||||
#TODO : alternative @app.on_event("startup") ?
|
#TODO : alternative @app.on_event("startup") ?
|
||||||
|
|||||||
0
server/src/app/models/__init__.py
Normal file
0
server/src/app/models/__init__.py
Normal file
BIN
server/src/app/models/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
server/src/app/models/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -4,3 +4,4 @@ class User(SQLModel, table=True):
|
|||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
username: str
|
username: str
|
||||||
hashed_password: str
|
hashed_password: str
|
||||||
|
#is_active: bool
|
||||||
Binary file not shown.
Reference in New Issue
Block a user