Updated with audit table

This commit is contained in:
Saurab-Shrestha 2024-02-25 09:54:56 +05:45
parent 493963908d
commit 808a0fa6fc
11 changed files with 124 additions and 14 deletions

View File

@ -0,0 +1,32 @@
"""added password expiry
Revision ID: b7b23e5d5214
Revises: 62c1a29320fc
Create Date: 2024-02-24 19:47:49.069873
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'b7b23e5d5214'
down_revision: Union[str, None] = '62c1a29320fc'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
# op.create_unique_constraint('unique_user_role', 'user_roles', ['user_id', 'role_id', 'company_id'])
op.add_column('users', sa.Column('password_created', sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'password_created')
# op.drop_constraint('unique_user_role', 'user_roles', type_='unique')
# ### end Alembic commands ###

View File

@ -220,7 +220,6 @@ def ingest_file(
object="list", model="private-gpt", data=ingested_documents) object="list", model="private-gpt", data=ingested_documents)
log_audit(model='Document', action='create', log_audit(model='Document', action='create',
details={ details={
'status': '200',
'filename': file.filename, 'filename': file.filename,
'user': current_user.fullname, 'user': current_user.fullname,
}, user_id=current_user.id) }, user_id=current_user.id)
@ -242,7 +241,6 @@ def ingest_file(
async def common_ingest_logic( async def common_ingest_logic(
request: Request, request: Request,
db: Session, db: Session,
ocr_file, ocr_file,
current_user, current_user,
@ -274,10 +272,10 @@ async def common_ingest_logic(
with open(upload_path, "wb") as f: with open(upload_path, "wb") as f:
f.write(file.read()) f.write(file.read())
file.seek(0) # Move the file pointer back to the beginning file.seek(0)
ingested_documents = service.ingest_bin_data(file_name, file) ingested_documents = service.ingest_bin_data(file_name, file)
log_audit(model='Document', action='create', log_audit(model='Document', action='create',
details={'status': 200, 'message': "file uploaded successfully."}, user_id=current_user.id) details={'status': "SUCCESS", 'message': f"{file_name} uploaded successfully."}, user_id=current_user.id)
logger.info( logger.info(
f"{file_name} is uploaded by the {current_user.fullname}.") f"{file_name} is uploaded by the {current_user.fullname}.")

View File

@ -139,3 +139,11 @@ def get_audit_logger(request: Request, db: Session = Depends(get_db)):
return lambda model, action, details, user_id=None: log_audit_entry(db, model, action, details, user_id) return lambda model, action, details, user_id=None: log_audit_entry(db, model, action, details, user_id)
except Exception as e: except Exception as e:
raise HTTPException(status_code=500, detail=f"Error in get_audit_logger: {str(e)}") raise HTTPException(status_code=500, detail=f"Error in get_audit_logger: {str(e)}")
def get_current_active_user(
current_user: models.User = Security(get_current_user, scopes=[],),
) -> models.User:
if not crud.user.is_active(current_user):
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

View File

@ -169,7 +169,7 @@ def login_access_token(
"user": token_payload, "user": token_payload,
"token_type": "bearer", "token_type": "bearer",
} }
log_audit(model='User', action='update', log_audit(model='User', action='login',
details=token_payload, user_id=user.id) details=token_payload, user_id=user.id)
return JSONResponse(content=response_dict) return JSONResponse(content=response_dict)
@ -213,7 +213,7 @@ def register(
role_name: str = Body(None, title="Role Name", role_name: str = Body(None, title="Role Name",
description="User role name (if applicable)"), description="User role name (if applicable)"),
current_user: models.User = Security( current_user: models.User = Security(
deps.get_current_user, deps.get_current_active_user,
scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]], scopes=[Role.ADMIN["name"], Role.SUPER_ADMIN["name"]],
), ),
) -> Any: ) -> Any:

View File

@ -45,8 +45,31 @@ def list_departments(
""" """
Retrieve a list of departments with pagination support. Retrieve a list of departments with pagination support.
""" """
departments = crud.department.get_multi(db, skip=skip, limit=limit) try:
return departments role = current_user.user_role.role.name if current_user.user_role else None
if role == "SUPER_ADMIN":
deps = crud.department.get_multi(db, skip=skip, limit=limit)
else:
deps = crud.department.get_multi_department(
db, department_id=current_user.department_id, skip=skip, limit=limit)
deps = [
schemas.Department(
id=dep.id,
name=dep.name,
total_users=dep.total_users,
total_documents=dep.total_documents,
)
for dep in deps
]
return deps
except Exception as e:
print(traceback.format_exc())
logger.error(f"There was an error listing the file(s).")
raise HTTPException(
status_code=500,
detail="Internal Server Error",
)
@router.post("/create", response_model=schemas.Department) @router.post("/create", response_model=schemas.Department)

View File

@ -7,7 +7,7 @@ from typing import Dict, Any, Optional, Union
from jose import JWTError, jwt from jose import JWTError, jwt
from passlib.context import CryptContext from passlib.context import CryptContext
ACCESS_TOKEN_EXPIRE_MINUTES = 120 # 30 minutes ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 12 # 12 hrs
REFRESH_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7 days REFRESH_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7 # 7 days
ALGORITHM = "HS256" ALGORITHM = "HS256"
# JWT_SECRET_KEY = os.environ['JWT_SECRET_KEY'] # should be kept secret # JWT_SECRET_KEY = os.environ['JWT_SECRET_KEY'] # should be kept secret

View File

@ -2,7 +2,7 @@ from sqlalchemy.orm import Session
from private_gpt.users.schemas.department import DepartmentCreate, DepartmentUpdate from private_gpt.users.schemas.department import DepartmentCreate, DepartmentUpdate
from private_gpt.users.models.department import Department from private_gpt.users.models.department import Department
from private_gpt.users.crud.base import CRUDBase from private_gpt.users.crud.base import CRUDBase
from typing import Optional from typing import Optional, List
class CRUDDepartments(CRUDBase[Department, DepartmentCreate, DepartmentUpdate]): class CRUDDepartments(CRUDBase[Department, DepartmentCreate, DepartmentUpdate]):
@ -12,4 +12,16 @@ class CRUDDepartments(CRUDBase[Department, DepartmentCreate, DepartmentUpdate]):
def get_by_department_name(self, db: Session, *, name: str) -> Optional[Department]: def get_by_department_name(self, db: Session, *, name: str) -> Optional[Department]:
return db.query(self.model).filter(Department.name == name).first() return db.query(self.model).filter(Department.name == name).first()
def get_multi_department(
self, db: Session, *, department_id: int, skip: int = 0, limit: int = 100
) -> List[Department]:
return (
db.query(self.model)
.filter(Department.department_id == department_id)
.offset(skip)
.limit(limit)
.all()
)
department = CRUDDepartments(Department) department = CRUDDepartments(Department)

View File

@ -13,6 +13,7 @@ from sqlalchemy import event, func, select, update
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from private_gpt.users.db.base_class import Base from private_gpt.users.db.base_class import Base
from private_gpt.users.models.department import Department from private_gpt.users.models.department import Department
class User(Base): class User(Base):
"""Models a user table""" """Models a user table"""
__tablename__ = "users" __tablename__ = "users"
@ -34,6 +35,8 @@ class User(Base):
default=datetime.datetime.utcnow, default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow,
) )
password_created = Column(DateTime, nullable=True)
company_id = Column(Integer, ForeignKey("companies.id"), nullable=True) company_id = Column(Integer, ForeignKey("companies.id"), nullable=True)
company = relationship("Company", back_populates="users") company = relationship("Company", back_populates="users")
@ -72,3 +75,29 @@ def update_total_users(mapper, connection, target):
) )
@event.listens_for(User, 'before_insert')
def set_password_created(mapper, connection, target):
target.password_created = datetime.datetime.utcnow()
connection.execute(
update(User)
.values(password_created=datetime.datetime.utcnow())
.where(User.id == target.id)
)
@event.listens_for(User, 'before_update', propagate=True)
def check_password_expiry(mapper, connection, target):
if target.password_created and (
datetime.datetime.utcnow() - target.password_created).days > 90:
target.is_active = False
connection.execute(
update(User)
.values(is_active=False)
.where(User.id == target.id)
)
else:
connection.execute(
update(User)
.values(is_active=True)
.where(User.id == target.id)
)

View File

@ -1,6 +1,6 @@
from .role import Role, RoleCreate, RoleInDB, RoleUpdate from .role import Role, RoleCreate, RoleInDB, RoleUpdate
from .token import TokenSchema, TokenPayload from .token import TokenSchema, TokenPayload
from .user import User, UserCreate, UserInDB, UserUpdate, UserBaseSchema, Profile, UsernameUpdate, DeleteUser, UserAdminUpdate, UserAdmin from .user import User, UserCreate, UserInDB, UserUpdate, UserBaseSchema, Profile, UsernameUpdate, DeleteUser, UserAdminUpdate, UserAdmin, PasswordUpdate
from .user_role import UserRole, UserRoleCreate, UserRoleInDB, UserRoleUpdate from .user_role import UserRole, UserRoleCreate, UserRoleInDB, UserRoleUpdate
from .subscription import Subscription, SubscriptionBase, SubscriptionCreate, SubscriptionUpdate from .subscription import Subscription, SubscriptionBase, SubscriptionCreate, SubscriptionUpdate
from .company import Company, CompanyBase, CompanyCreate, CompanyUpdate from .company import Company, CompanyBase, CompanyCreate, CompanyUpdate

View File

@ -33,6 +33,10 @@ class DepartmentAdminCreate(DepartmentBase):
class Config: class Config:
orm_mode = True orm_mode = True
class Department(DepartmentInDB): class Department(BaseModel):
pass id: int
name: str
total_users: Optional[int]
total_documents: Optional[int]

View File

@ -75,4 +75,8 @@ class UserAdminUpdate(BaseModel):
class UserAdmin(BaseModel): class UserAdmin(BaseModel):
fullname: str fullname: str
department_id: int department_id: int
class PasswordUpdate(BaseModel):
password_created: Optional[datetime] = None