add user restriction
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,18 +1,18 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, APIRouter, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
from src.app.models.user import User
|
||||
from src.app.data.user import create_user
|
||||
from src.app.models.user import User, UserCreate
|
||||
from src.app.data.user import create_user, get_user_by_username
|
||||
|
||||
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(prefix="/auth", tags=["auth"])
|
||||
|
||||
@router.post("/token")
|
||||
@router.post("/login")
|
||||
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> Token:
|
||||
user = authenticate_user(form_data.username, form_data.password)
|
||||
if not user:
|
||||
@@ -24,15 +24,19 @@ async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]) -> T
|
||||
access_token = create_access_token(data={"sub": user.username})
|
||||
return Token(access_token=access_token, token_type="bearer")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/v1/token")
|
||||
|
||||
@router.get("/user")
|
||||
@router.get("/me")
|
||||
async def user(current_user: Annotated[str, Depends(get_current_user)]):
|
||||
return current_user
|
||||
|
||||
@router.post("/user")
|
||||
async def create(username, password):
|
||||
hashed_password = await hash_password(password)
|
||||
user = User(username = username, hashed_password = hashed_password)
|
||||
@router.post("/register")
|
||||
async def create(user_data: UserCreate):
|
||||
if get_user_by_username(user_data.username):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Username already registered"
|
||||
)
|
||||
|
||||
hashed_password = hash_password(user_data.plain_password)
|
||||
user = User(username = user_data.username, hashed_password = hashed_password)
|
||||
created_user = create_user(user)
|
||||
return created_user
|
||||
@@ -1,48 +1,47 @@
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from src.app.models.knowledge import Knowledge
|
||||
from src.app.auth.dependancies import get_current_user
|
||||
|
||||
from src.app.models.knowledge import Knowledge, KnowledgeCreate
|
||||
from src.app.models.question import Question
|
||||
|
||||
from src.app.data.knowledge import create_knowledge, read_knowledges, read_knowledge, update_knowledge, delete_knowledge
|
||||
from src.app.data.question import read_questions as read_questions_crud, create_question
|
||||
from src.app.data.knowledge import create_knowledge, get_knowledges_by_user, get_knowledge_by_id
|
||||
#from src.app.data.knowledge update_knowledge, delete_knowledge
|
||||
from src.app.data.question import get_questions, create_question
|
||||
|
||||
from src.app.services.language_generation import questions_generation
|
||||
|
||||
#Added in __ini__
|
||||
router = APIRouter(tags=["knowledges"])
|
||||
|
||||
@router.get("/knowledges/")
|
||||
def read(current_user: Annotated[str, Depends(get_current_user)]):
|
||||
knowledges = get_knowledges_by_user(current_user)
|
||||
return knowledges
|
||||
|
||||
@router.get("/knowledges/{id}")
|
||||
def read(id: int, current_user: Annotated[str, Depends(get_current_user)]):
|
||||
knowledge = get_knowledge_by_id(id, current_user)
|
||||
return knowledge
|
||||
|
||||
@router.post("/knowledges/")
|
||||
def create(knowledge: Knowledge):
|
||||
def create(knowledge_data: KnowledgeCreate, current_user: Annotated[str, Depends(get_current_user)]):
|
||||
knowledge = Knowledge(content=knowledge_data.content, uri=knowledge_data.uri, user=current_user)
|
||||
created_knowledge = create_knowledge(knowledge)
|
||||
# if created_knowledge is None:
|
||||
# raise NotFoundException("Failed to create knowledge")
|
||||
return created_knowledge
|
||||
|
||||
@router.get("/knowledges/")
|
||||
def read():
|
||||
knowledges = read_knowledges()
|
||||
return knowledges
|
||||
|
||||
@router.get("/knowledges/{id}")
|
||||
def read(id: int):
|
||||
knowledge = read_knowledge(id)
|
||||
return knowledge
|
||||
|
||||
#TODO: adapt with correct pattern
|
||||
@router.post("/knowledges/{id}")
|
||||
def update(id: int, content: str, uri: str):
|
||||
knowledge = update_knowledge(id, content, uri)
|
||||
return knowledge
|
||||
|
||||
@router.delete("/knowledges/{id}")
|
||||
def delete(id: int):
|
||||
knowledge = delete_knowledge(id)
|
||||
return knowledge
|
||||
|
||||
@router.post("/knowledges/{id}/questions")
|
||||
def create_questions(id: int):
|
||||
knowledge = read_knowledge(id)
|
||||
def generate_questions(id: int, current_user: Annotated[str, Depends(get_current_user)]):
|
||||
knowledge: Knowledge = get_knowledge_by_id(id, current_user)
|
||||
if not knowledge:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Forbidden. The requested knowledge is not available for the provided ID.",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
questions_raw = questions_generation(knowledge)
|
||||
for q in questions_raw:
|
||||
question = Question(question = q, knowledge=knowledge)
|
||||
@@ -50,9 +49,24 @@ def create_questions(id: int):
|
||||
return questions_raw
|
||||
|
||||
@router.get("/knowledges/{id}/questions")
|
||||
def read_questions(id: int):
|
||||
knowledge: Knowledge = read_knowledge(id)
|
||||
#questions = knowledge.questions
|
||||
#TODO : refacto ?
|
||||
questions = read_questions_crud(knowledge)
|
||||
return questions
|
||||
def read_questions(id: int, current_user: Annotated[str, Depends(get_current_user)]):
|
||||
knowledge: Knowledge = get_knowledge_by_id(id, current_user)
|
||||
if not knowledge:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Forbidden. The requested knowledge is not available for the provided ID.",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
questions = get_questions(knowledge)
|
||||
return questions
|
||||
|
||||
#TODO: adapt with correct pattern
|
||||
# @router.post("/knowledges/{id}")
|
||||
# def update(id: int, content: str, uri: str):
|
||||
# knowledge = update_knowledge(id, content, uri)
|
||||
# return knowledge
|
||||
|
||||
# @router.delete("/knowledges/{id}")
|
||||
# def delete(id: int):
|
||||
# knowledge = delete_knowledge(id)
|
||||
# return knowledge
|
||||
@@ -1,11 +1,15 @@
|
||||
from fastapi import APIRouter
|
||||
from typing import Annotated
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from src.app.models.metric import Metric
|
||||
from src.app.models.metric import Metric, MetricCreate
|
||||
from src.app.data.metric import create_metric
|
||||
|
||||
from src.app.auth.dependancies import get_current_user
|
||||
|
||||
router = APIRouter(tags=["metrics"])
|
||||
|
||||
@router.post("/metrics/")
|
||||
def create(metric: Metric):
|
||||
def create(metric_data: MetricCreate, current_user: Annotated[str, Depends(get_current_user)]):
|
||||
metric: Metric = Metric(question_id = metric_data.question_id, need_index = metric_data.need_index, user = current_user)
|
||||
created_metric: Metric = create_metric(metric)
|
||||
return created_metric
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,23 +1,19 @@
|
||||
from src.app.config import settings
|
||||
|
||||
from typing import Annotated
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from argon2 import PasswordHasher
|
||||
from argon2 import PasswordHasher
|
||||
import jwt
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
|
||||
from src.app.models.user import User
|
||||
from src.app.data.user import get_user
|
||||
from src.app.data.user import get_user_by_username
|
||||
from .schemas import TokenData
|
||||
from .security import verify_password
|
||||
from .security import verify_password, verify_token
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/token")
|
||||
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")
|
||||
password_hasher = PasswordHasher()
|
||||
|
||||
def authenticate_user(username: str, password: str):
|
||||
user: User = get_user(username)
|
||||
user: User = get_user_by_username(username)
|
||||
if not user:
|
||||
# Add timing to prevent attack
|
||||
password_hasher.hash(password)
|
||||
@@ -32,17 +28,19 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> Use
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
except InvalidTokenError:
|
||||
payload = verify_token(token, token_type="access")
|
||||
if payload is None:
|
||||
raise credentials_exception
|
||||
user = get_user(username=token_data.username)
|
||||
|
||||
username = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
|
||||
user = get_user_by_username(username=token_data.username)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
|
||||
return user
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from src.app.config import settings
|
||||
from typing import Optional
|
||||
from datetime import timedelta, datetime, timezone
|
||||
from argon2 import PasswordHasher
|
||||
from argon2.exceptions import (
|
||||
@@ -10,6 +11,7 @@ import jwt
|
||||
from jwt.exceptions import InvalidTokenError
|
||||
|
||||
|
||||
|
||||
password_hasher = PasswordHasher()
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
@@ -28,6 +30,11 @@ def create_access_token(data: dict):
|
||||
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]:
|
||||
try:
|
||||
payload = jwt.decode(jwt = token, key = settings.SECRET_KEY, algorithms = [settings.ALGORITHM])
|
||||
return payload
|
||||
except InvalidTokenError:
|
||||
return None
|
||||
|
||||
#def verify_token(token: str, token_type: str = "access") -> Optional[dict]:
|
||||
#def create_refresh_token(data: dict) -> str:
|
||||
@@ -12,7 +12,7 @@ class Settings(BaseSettings):
|
||||
# Security
|
||||
ORIGIN: str = Field('http://localhost:5173', env='ORIGIN')
|
||||
SECRET_KEY : str = Field('random_string', env='SECRET_KEY')
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 240
|
||||
ALGORITHM: str = "HS256"
|
||||
|
||||
class Config:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,36 @@
|
||||
from fastapi import Depends
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from src.app.models.knowledge import Knowledge
|
||||
from src.app.models.user import User
|
||||
from src.app.database import engine
|
||||
|
||||
def get_knowledge_by_id(knowledge_id: int, user: User):
|
||||
with Session(engine) as session:
|
||||
statement = select(Knowledge).where(Knowledge.id == knowledge_id, Knowledge.user_id == user.id)
|
||||
results = session.exec(statement)
|
||||
knowledge = results.first()
|
||||
return knowledge
|
||||
|
||||
# def get_knowledge_by_id(knowledge_id: int):
|
||||
# with Session(engine) as session:
|
||||
# knowledge = session.get(Knowledge, knowledge_id)
|
||||
# return knowledge
|
||||
|
||||
# No filter by user
|
||||
def get_knowledges():
|
||||
with Session(engine) as session:
|
||||
statement = select(Knowledge)
|
||||
results = session.exec(statement)
|
||||
knowledges = results.all()
|
||||
return knowledges
|
||||
|
||||
def get_knowledges_by_user(user: User):
|
||||
with Session(engine) as session:
|
||||
statement = select(Knowledge).where(Knowledge.user_id == user.id)
|
||||
results = session.exec(statement)
|
||||
knowledges = results.all()
|
||||
return knowledges
|
||||
|
||||
def create_knowledge(knowledge: Knowledge):
|
||||
with Session(engine) as session:
|
||||
session.add(knowledge)
|
||||
@@ -11,32 +38,23 @@ def create_knowledge(knowledge: Knowledge):
|
||||
session.refresh(knowledge)
|
||||
return knowledge
|
||||
|
||||
def read_knowledges():
|
||||
with Session(engine) as session:
|
||||
statement = select(Knowledge)
|
||||
results = session.exec(statement)
|
||||
knowledges = results.all()
|
||||
return knowledges
|
||||
|
||||
def read_knowledge(knowledge_id: int):
|
||||
with Session(engine) as session:
|
||||
knowledge = session.get(Knowledge, knowledge_id)
|
||||
return knowledge
|
||||
|
||||
#TODO adapt logic with args
|
||||
def update_knowledge(knowledge_id: int, content: str, uri: str):
|
||||
with Session(engine) as session:
|
||||
knowledge = session.get(Knowledge, knowledge_id)
|
||||
knowledge.content = content if content else knowledge.content
|
||||
knowledge.uri = uri if uri else knowledge.uri
|
||||
# No filter by user
|
||||
# Transform to update_knowledge(knowledge: Knowledge)
|
||||
# def update_knowledge(knowledge_id: int, content: str, uri: str):
|
||||
# with Session(engine) as session:
|
||||
# knowledge = session.get(Knowledge, knowledge_id)
|
||||
# knowledge.content = content if content else knowledge.content
|
||||
# knowledge.uri = uri if uri else knowledge.uri
|
||||
|
||||
session.add(knowledge)
|
||||
session.commit()
|
||||
session.refresh(knowledge)
|
||||
return knowledge
|
||||
# session.add(knowledge)
|
||||
# session.commit()
|
||||
# session.refresh(knowledge)
|
||||
# return knowledge
|
||||
|
||||
def delete_knowledge(knowledge_id: int):
|
||||
with Session(engine) as session:
|
||||
knowledge = session.get(Knowledge, knowledge_id)
|
||||
session.delete(knowledge)
|
||||
session.commit()
|
||||
# No filter by user
|
||||
# def delete_knowledge(knowledge_id: int):
|
||||
# with Session(engine) as session:
|
||||
# knowledge = session.get(Knowledge, knowledge_id)
|
||||
# session.delete(knowledge)
|
||||
# session.commit()
|
||||
@@ -4,24 +4,24 @@ from src.app.models.metric import Metric
|
||||
from src.app.models.question import Question
|
||||
from src.app.database import engine
|
||||
|
||||
def get_metric_by_id(metric_id: int):
|
||||
with Session(engine) as session:
|
||||
metric = session.get(Metric, metric_id)
|
||||
return metric
|
||||
|
||||
def get_metrics(question):
|
||||
with Session(engine) as session:
|
||||
statement = select(Metric).where(Metric.question_id == question.id)
|
||||
results = session.exec(statement)
|
||||
metrics = results.all()
|
||||
return metrics
|
||||
|
||||
def create_metric(metric: Metric):
|
||||
with Session(engine) as session:
|
||||
session.add(metric)
|
||||
session.commit()
|
||||
session.refresh(metric)
|
||||
return metric
|
||||
|
||||
def read_metrics(question):
|
||||
with Session(engine) as session:
|
||||
statement = select(Metric).where(Metric.question_id == question.id)
|
||||
results = session.exec(statement)
|
||||
metrics = results.all()
|
||||
return metrics
|
||||
|
||||
def read_metric(metric_id: int):
|
||||
with Session(engine) as session:
|
||||
metric = session.get(Metric, metric_id)
|
||||
return metric
|
||||
|
||||
def delete_metric(metric_id: int):
|
||||
with Session(engine) as session:
|
||||
|
||||
@@ -3,23 +3,23 @@ from sqlmodel import Session, select
|
||||
from src.app.models.question import Question
|
||||
from src.app.database import engine
|
||||
|
||||
def create_question(question: Question):
|
||||
def get_question_by_id(question_id: int):
|
||||
with Session(engine) as session:
|
||||
session.add(question)
|
||||
session.commit()
|
||||
session.refresh(question)
|
||||
question = session.get(Question, question_id)
|
||||
return question
|
||||
|
||||
def read_questions(knowledge):
|
||||
|
||||
def get_questions(knowledge):
|
||||
with Session(engine) as session:
|
||||
statement = select(Question).where(Question.knowledge_id == knowledge.id)
|
||||
results = session.exec(statement)
|
||||
questions = results.all()
|
||||
return questions
|
||||
|
||||
def read_question(question_id: int):
|
||||
def create_question(question: Question):
|
||||
with Session(engine) as session:
|
||||
question = session.get(Question, question_id)
|
||||
session.add(question)
|
||||
session.commit()
|
||||
session.refresh(question)
|
||||
return question
|
||||
|
||||
def delete_question(question_id: int):
|
||||
|
||||
@@ -10,14 +10,14 @@ def create_user(user: User):
|
||||
session.refresh(user)
|
||||
return user
|
||||
|
||||
def get_user(username: str):
|
||||
def get_user_by_username(username: str):
|
||||
with Session(engine) as session:
|
||||
statement = select(User).where(User.username == username)
|
||||
results = session.exec(statement)
|
||||
result = results.first()
|
||||
return result
|
||||
|
||||
def read_users():
|
||||
def get_users():
|
||||
with Session(engine) as session:
|
||||
statement = select(User)
|
||||
results = session.exec(statement)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,17 @@
|
||||
from sqlmodel import Field, SQLModel, Relationship
|
||||
from pydantic import BaseModel
|
||||
from src.app.models.user import User
|
||||
|
||||
class Knowledge(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
content: str = Field(index=True)
|
||||
uri: str = Field(index=True)
|
||||
|
||||
questions: list["Question"] = Relationship(back_populates="knowledge", cascade_delete=True) # type: ignore
|
||||
questions: list["Question"] = Relationship(back_populates="knowledge", cascade_delete=True) # type: ignore
|
||||
|
||||
user_id: int | None = Field(default=None, foreign_key="user.id", ondelete="CASCADE")
|
||||
user: User | None = Relationship(back_populates="knowledges")
|
||||
|
||||
class KnowledgeCreate(BaseModel):
|
||||
content: str
|
||||
uri:str
|
||||
@@ -1,5 +1,7 @@
|
||||
from sqlmodel import Field, SQLModel, Relationship
|
||||
from pydantic import BaseModel
|
||||
from src.app.models.question import Question
|
||||
from src.app.models.user import User
|
||||
|
||||
class Metric(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
@@ -8,3 +10,10 @@ class Metric(SQLModel, table=True):
|
||||
question: Question | None = Relationship(back_populates="metrics")
|
||||
|
||||
need_index: int
|
||||
|
||||
user_id: int | None = Field(default=None, foreign_key="user.id", ondelete="CASCADE")
|
||||
user: User | None = Relationship(back_populates="metrics")
|
||||
|
||||
class MetricCreate(BaseModel):
|
||||
question_id: int
|
||||
need_index: int
|
||||
@@ -1,5 +1,6 @@
|
||||
from sqlmodel import Field, SQLModel, Relationship
|
||||
from src.app.models.knowledge import Knowledge
|
||||
from src.app.models.user import User
|
||||
|
||||
class Question(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
@@ -9,3 +10,6 @@ class Question(SQLModel, table=True):
|
||||
knowledge: Knowledge | None = Relationship(back_populates="questions")
|
||||
|
||||
metrics: list["Metric"] = Relationship(back_populates="question", cascade_delete=True) # type: ignore
|
||||
|
||||
user_id: int | None = Field(default=None, foreign_key="user.id", ondelete="CASCADE")
|
||||
user: User | None = Relationship(back_populates="questions")
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
from sqlmodel import Field, SQLModel, Relationship
|
||||
from pydantic import BaseModel
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
username: str
|
||||
hashed_password: str
|
||||
#is_active: bool
|
||||
#is_active: bool
|
||||
|
||||
knowledges: list["Knowledge"] = Relationship(back_populates="user", cascade_delete=True) # type: ignore
|
||||
questions: list["Question"] = Relationship(back_populates="user", cascade_delete=True) # type: ignore
|
||||
metrics: list["Metric"] = Relationship(back_populates="user", cascade_delete=True) # type: ignore
|
||||
|
||||
class UserCreate(BaseModel):
|
||||
username: str
|
||||
plain_password: str
|
||||
Reference in New Issue
Block a user