mirror of
https://github.com/imartinez/privateGPT.git
synced 2025-06-30 17:22:43 +00:00
Added change password
This commit is contained in:
parent
e5edaadf24
commit
c66feb4187
@ -1,9 +1,12 @@
|
|||||||
|
import logging
|
||||||
|
from private_gpt.users.core.config import settings
|
||||||
|
from private_gpt.users.constants.role import Role
|
||||||
from typing import Union, Any, Generator
|
from typing import Union, Any, Generator
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from private_gpt.users import crud, models, schemas
|
from private_gpt.users import crud, models, schemas
|
||||||
from private_gpt.users.db.session import SessionLocal
|
from private_gpt.users.db.session import SessionLocal
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
from fastapi.security import OAuth2PasswordBearer
|
from fastapi.security import OAuth2PasswordBearer, SecurityScopes
|
||||||
from private_gpt.users.core.security import (
|
from private_gpt.users.core.security import (
|
||||||
ALGORITHM,
|
ALGORITHM,
|
||||||
JWT_SECRET_KEY
|
JWT_SECRET_KEY
|
||||||
@ -14,11 +17,21 @@ from pydantic import ValidationError
|
|||||||
from private_gpt.users.schemas.token import TokenPayload
|
from private_gpt.users.schemas.token import TokenPayload
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
reuseable_oauth = OAuth2PasswordBearer(
|
reusable_oauth2 = OAuth2PasswordBearer(
|
||||||
tokenUrl="/login",
|
tokenUrl=f"{settings.API_V1_STR}/auth/login",
|
||||||
scheme_name="JWT"
|
scopes={
|
||||||
|
Role.GUEST["name"]: Role.GUEST["description"],
|
||||||
|
# Role.ACCOUNT_ADMIN["name"]: Role.ACCOUNT_ADMIN["description"],
|
||||||
|
# Role.ACCOUNT_MANAGER["name"]: Role.ACCOUNT_MANAGER["description"],
|
||||||
|
Role.ADMIN["name"]: Role.ADMIN["description"],
|
||||||
|
Role.SUPER_ADMIN["name"]: Role.SUPER_ADMIN["description"],
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_db() -> Generator:
|
def get_db() -> Generator:
|
||||||
try:
|
try:
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
@ -27,39 +40,55 @@ def get_db() -> Generator:
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
async def get_current_user(
|
async def get_current_user(
|
||||||
|
security_scopes: SecurityScopes,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
token: str = Depends(reuseable_oauth)
|
token: str = Depends(reusable_oauth2)
|
||||||
) -> models.User:
|
) -> models.User:
|
||||||
|
if security_scopes.scopes:
|
||||||
|
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||||
|
else:
|
||||||
|
authenticate_value = "Bearer"
|
||||||
|
|
||||||
|
credentials_exception = HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail="os",
|
||||||
|
headers={"WWW-Authenticate": authenticate_value},
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(
|
payload = jwt.decode(
|
||||||
token, JWT_SECRET_KEY, algorithms=[ALGORITHM]
|
token, JWT_SECRET_KEY, algorithms=[ALGORITHM]
|
||||||
)
|
)
|
||||||
token_data = TokenPayload(**payload)
|
if payload.get("id") is None:
|
||||||
|
raise credentials_exception
|
||||||
if datetime.fromtimestamp(token_data.exp) < datetime.now():
|
print(payload)
|
||||||
raise HTTPException(
|
token_data = schemas.TokenPayload(**payload)
|
||||||
status_code = status.HTTP_401_UNAUTHORIZED,
|
except (jwt.JWTError, ValidationError):
|
||||||
detail="Token expired",
|
logger.error("Error Decoding Token", exc_info=True)
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
|
||||||
)
|
|
||||||
except(jwt.JWTError, ValidationError):
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail="Could not validate credentials",
|
detail="Could not validate credentials",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
|
||||||
)
|
)
|
||||||
|
user = crud.user.get(db, id=token_data.id)
|
||||||
user: Union[dict[str, Any], None] = crud.user.get(db, id=token_data.id)
|
if not user:
|
||||||
|
raise credentials_exception
|
||||||
if user is None:
|
if security_scopes.scopes and not token_data.role:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND,
|
status_code=401,
|
||||||
detail="Could not find user",
|
detail="Not enough permissions",
|
||||||
|
headers={"WWW-Authenticate": authenticate_value},
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
security_scopes.scopes
|
||||||
|
and token_data.role not in security_scopes.scopes
|
||||||
|
):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail="Not enough permissions",
|
||||||
|
headers={"WWW-Authenticate": authenticate_value},
|
||||||
)
|
)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
async def get_current_active_user(
|
async def get_current_active_user(
|
||||||
current_user: models.User = Security(get_current_user, scopes=[],),
|
current_user: models.User = Security(get_current_user, scopes=[],),
|
||||||
) -> models.User:
|
) -> models.User:
|
||||||
|
@ -64,7 +64,8 @@ def login_access_token(
|
|||||||
),
|
),
|
||||||
"refresh_token": security.create_refresh_token(
|
"refresh_token": security.create_refresh_token(
|
||||||
token_payload, expires_delta=refresh_token_expires
|
token_payload, expires_delta=refresh_token_expires
|
||||||
)
|
),
|
||||||
|
"token_type": "bearer",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ def register(
|
|||||||
user = crud.user.create(db, obj_in=user_in)
|
user = crud.user.create(db, obj_in=user_in)
|
||||||
|
|
||||||
# get role
|
# get role
|
||||||
role = crud.role.get_by_name(db, name=Role.GUEST["name"])
|
role = crud.role.get_by_name(db, name=Role.SUPER_ADMIN["name"])
|
||||||
print("ROLE:", role)
|
print("ROLE:", role)
|
||||||
# assign user_role
|
# assign user_role
|
||||||
user_role_in = schemas.UserRoleCreate(
|
user_role_in = schemas.UserRoleCreate(
|
||||||
@ -127,5 +128,6 @@ def register(
|
|||||||
),
|
),
|
||||||
"refresh_token": security.create_refresh_token(
|
"refresh_token": security.create_refresh_token(
|
||||||
token_payload, expires_delta=refresh_token_expires
|
token_payload, expires_delta=refresh_token_expires
|
||||||
)
|
),
|
||||||
|
"token_type": "bearer",
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,10 @@ from private_gpt.users.api import deps
|
|||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(prefix='/roles', tags=['roles'])
|
router = APIRouter(prefix='/roles', tags=['roles'])
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", response_model=List[schemas.Role])
|
@router.get("/", response_model=List[schemas.Role])
|
||||||
def get_roles(
|
def get_roles(
|
||||||
db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100,
|
db: Session = Depends(deps.get_db), skip: int = 0, limit: int = 100,
|
||||||
|
@ -14,7 +14,7 @@ def assign_user_role(
|
|||||||
*,
|
*,
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
user_role_in: schemas.UserRoleCreate,
|
user_role_in: schemas.UserRoleCreate,
|
||||||
current_user: models.User = Depends(deps.get_current_active_user),
|
current_user: models.User = Depends(deps.get_current_user),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Assign a role to a user after creation of a user
|
Assign a role to a user after creation of a user
|
||||||
@ -35,14 +35,13 @@ def update_user_role(
|
|||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
user_id: int,
|
user_id: int,
|
||||||
user_role_in: schemas.UserRoleUpdate,
|
user_role_in: schemas.UserRoleUpdate,
|
||||||
# current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
# deps.get_current_active_user,
|
deps.get_current_user,
|
||||||
# scopes=[
|
scopes=[
|
||||||
# Role.ADMIN["name"],
|
Role.ADMIN["name"],
|
||||||
# Role.SUPER_ADMIN["name"],
|
Role.SUPER_ADMIN["name"],
|
||||||
# Role.ACCOUNT_ADMIN["name"],
|
],
|
||||||
# ],
|
),
|
||||||
# ),
|
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Update a users role.
|
Update a users role.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
from private_gpt.users import crud, models, schemas
|
from private_gpt.users import crud, models, schemas
|
||||||
from private_gpt.users.api import deps
|
from private_gpt.users.api import deps
|
||||||
from private_gpt.users.constants.role import Role
|
from private_gpt.users.constants.role import Role
|
||||||
from private_gpt.users.core.config import settings
|
from private_gpt.users.core.config import settings
|
||||||
|
from private_gpt.users.core.security import verify_password, get_password_hash
|
||||||
from fastapi import APIRouter, Body, Depends, HTTPException, Security
|
from fastapi import APIRouter, Body, Depends, HTTPException, Security
|
||||||
from fastapi.encoders import jsonable_encoder
|
from fastapi.encoders import jsonable_encoder
|
||||||
from pydantic.networks import EmailStr
|
from pydantic.networks import EmailStr
|
||||||
@ -17,10 +17,10 @@ def read_users(
|
|||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
# current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
# deps.get_current_active_user,
|
deps.get_current_user,
|
||||||
# scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
||||||
# ),
|
),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Retrieve all users.
|
Retrieve all users.
|
||||||
@ -35,7 +35,7 @@ def create_user(
|
|||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
user_in: schemas.UserCreate,
|
user_in: schemas.UserCreate,
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
deps.get_current_active_user,
|
deps.get_current_user,
|
||||||
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
||||||
),
|
),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
@ -57,21 +57,20 @@ def update_user_me(
|
|||||||
*,
|
*,
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
fullname: str = Body(None),
|
fullname: str = Body(None),
|
||||||
phone_number: str = Body(None),
|
|
||||||
email: EmailStr = Body(None),
|
email: EmailStr = Body(None),
|
||||||
current_user: models.User = Depends(deps.get_current_active_user),
|
current_user: models.User = Depends(deps.get_current_user),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Update own user.
|
Update own user.
|
||||||
"""
|
"""
|
||||||
current_user_data = jsonable_encoder(current_user)
|
current_user_data = jsonable_encoder(current_user)
|
||||||
|
print("Current user data: ", current_user_data)
|
||||||
user_in = schemas.UserUpdate(**current_user_data)
|
user_in = schemas.UserUpdate(**current_user_data)
|
||||||
if phone_number is not None:
|
|
||||||
user_in.phone_number = phone_number
|
|
||||||
if fullname is not None:
|
if fullname is not None:
|
||||||
user_in.fullname = fullname
|
user_in.fullname = fullname
|
||||||
if email is not None:
|
if email is not None:
|
||||||
user_in.email = email
|
user_in.email = email
|
||||||
|
print(f"DB obj: {current_user}\n obj IN : {user_in}")
|
||||||
user = crud.user.update(db, db_obj=current_user, obj_in=user_in)
|
user = crud.user.update(db, db_obj=current_user, obj_in=user_in)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@ -79,7 +78,7 @@ def update_user_me(
|
|||||||
@router.get("/me", response_model=schemas.User)
|
@router.get("/me", response_model=schemas.User)
|
||||||
def read_user_me(
|
def read_user_me(
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
current_user: models.User = Depends(deps.get_current_active_user),
|
current_user: models.User = Depends(deps.get_current_user),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Get current user.
|
Get current user.
|
||||||
@ -95,25 +94,33 @@ def read_user_me(
|
|||||||
fullname=current_user.fullname,
|
fullname=current_user.fullname,
|
||||||
created_at=current_user.created_at,
|
created_at=current_user.created_at,
|
||||||
updated_at=current_user.updated_at,
|
updated_at=current_user.updated_at,
|
||||||
role=role,
|
last_login = current_user.last_login,
|
||||||
|
role=role
|
||||||
)
|
)
|
||||||
return user_data
|
return user_data
|
||||||
|
|
||||||
|
|
||||||
@router.get("/me/change-password", response_model=schemas.User)
|
@router.patch("/me/change-password", response_model=schemas.User)
|
||||||
def read_user_me(
|
def change_password(
|
||||||
|
*,
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
current_user: models.User = Depends(deps.get_current_active_user),
|
current_user: models.User = Depends(deps.get_current_user),
|
||||||
old_password: str = Body(None),
|
old_password: str = Body(..., embed=True),
|
||||||
new_password: str = Body(None),
|
new_password: str = Body(..., embed=True),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""
|
"""
|
||||||
Get current user.
|
Change current user's password.
|
||||||
"""
|
"""
|
||||||
if not current_user.user_role:
|
# Verify the old password
|
||||||
role = None
|
if not verify_password(old_password, current_user.hashed_password):
|
||||||
else:
|
raise HTTPException(status_code=400, detail="Old password is incorrect")
|
||||||
role = current_user.user_role.role.name
|
|
||||||
|
# Change the password
|
||||||
|
new_password_hashed = get_password_hash(new_password)
|
||||||
|
current_user.hashed_password = new_password_hashed
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
role = current_user.user_role.role.name if current_user.user_role else None
|
||||||
user_data = schemas.User(
|
user_data = schemas.User(
|
||||||
id=current_user.id,
|
id=current_user.id,
|
||||||
email=current_user.email,
|
email=current_user.email,
|
||||||
@ -121,15 +128,17 @@ def read_user_me(
|
|||||||
fullname=current_user.fullname,
|
fullname=current_user.fullname,
|
||||||
created_at=current_user.created_at,
|
created_at=current_user.created_at,
|
||||||
updated_at=current_user.updated_at,
|
updated_at=current_user.updated_at,
|
||||||
|
last_login=current_user.last_login,
|
||||||
role=role,
|
role=role,
|
||||||
)
|
)
|
||||||
return user_data
|
return user_data
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{user_id}", response_model=schemas.User)
|
@router.get("/{user_id}", response_model=schemas.User)
|
||||||
def read_user_by_id(
|
def read_user_by_id(
|
||||||
user_id: int,
|
user_id: int,
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
deps.get_current_active_user,
|
deps.get_current_user,
|
||||||
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
||||||
),
|
),
|
||||||
db: Session = Depends(deps.get_db),
|
db: Session = Depends(deps.get_db),
|
||||||
@ -137,6 +146,8 @@ def read_user_by_id(
|
|||||||
"""
|
"""
|
||||||
Get a specific user by id.
|
Get a specific user by id.
|
||||||
"""
|
"""
|
||||||
|
if user_id is None:
|
||||||
|
return "User id is not given."
|
||||||
user = crud.user.get(db, id=user_id)
|
user = crud.user.get(db, id=user_id)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@ -148,7 +159,7 @@ def update_user(
|
|||||||
user_id: int,
|
user_id: int,
|
||||||
user_in: schemas.UserUpdate,
|
user_in: schemas.UserUpdate,
|
||||||
current_user: models.User = Security(
|
current_user: models.User = Security(
|
||||||
deps.get_current_active_user,
|
deps.get_current_user,
|
||||||
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
|
||||||
),
|
),
|
||||||
) -> Any:
|
) -> Any:
|
||||||
|
@ -15,7 +15,7 @@ SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{username}:{password}@{host}:{p
|
|||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
PROJECT_NAME: str = "AUTHENTICATION AND AUTHORIZATION"
|
PROJECT_NAME: str = "AUTHENTICATION AND AUTHORIZATION"
|
||||||
API_V1_STR: str = "/api/v1"
|
API_V1_STR: str = "/v1"
|
||||||
SECRET_KEY: str
|
SECRET_KEY: str
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES: int
|
ACCESS_TOKEN_EXPIRE_MINUTES: int
|
||||||
REFRESH_TOKEN_EXPIRE_MINUTES: int
|
REFRESH_TOKEN_EXPIRE_MINUTES: int
|
||||||
|
@ -31,15 +31,14 @@ class UserLoginSchema(BaseModel):
|
|||||||
|
|
||||||
class UserSchema(UserBaseSchema):
|
class UserSchema(UserBaseSchema):
|
||||||
id: int
|
id: int
|
||||||
user_role: Optional[UserRole]
|
user_role: Optional[UserRole] = None
|
||||||
last_login: Optional[datetime]
|
last_login: Optional[datetime] = None
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
is_active: bool = Field(default=False)
|
is_active: bool = Field(default=False)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
json_exclude = {'user_role'}
|
|
||||||
|
|
||||||
# Additional properties to return via API
|
# Additional properties to return via API
|
||||||
class User(UserSchema):
|
class User(UserSchema):
|
||||||
|
Loading…
Reference in New Issue
Block a user