forked from phoenix/litellm-mirror
feat - working audit logs for create, update delete team
This commit is contained in:
parent
970c3dfdc7
commit
5bd658493f
6 changed files with 128 additions and 32 deletions
|
@ -60,6 +60,7 @@ _async_failure_callback: List[Callable] = (
|
||||||
pre_call_rules: List[Callable] = []
|
pre_call_rules: List[Callable] = []
|
||||||
post_call_rules: List[Callable] = []
|
post_call_rules: List[Callable] = []
|
||||||
turn_off_message_logging: Optional[bool] = False
|
turn_off_message_logging: Optional[bool] = False
|
||||||
|
store_audit_logs = False # Enterprise feature, allow users to see audit logs
|
||||||
## end of callbacks #############
|
## end of callbacks #############
|
||||||
|
|
||||||
email: Optional[str] = (
|
email: Optional[str] = (
|
||||||
|
|
|
@ -1278,6 +1278,21 @@ class LiteLLM_ErrorLogs(LiteLLMBase):
|
||||||
endTime: Union[str, datetime, None]
|
endTime: Union[str, datetime, None]
|
||||||
|
|
||||||
|
|
||||||
|
class LiteLLM_AuditLogs(LiteLLMBase):
|
||||||
|
id: str
|
||||||
|
updated_at: datetime
|
||||||
|
changed_by: str
|
||||||
|
action: Literal["created", "updated", "deleted"]
|
||||||
|
table_name: Literal[
|
||||||
|
LitellmTableNames.TEAM_TABLE_NAME,
|
||||||
|
LitellmTableNames.USER_TABLE_NAME,
|
||||||
|
LitellmTableNames.PROXY_MODEL_TABLE_NAME,
|
||||||
|
]
|
||||||
|
object_id: str
|
||||||
|
before_value: Optional[Json] = None
|
||||||
|
updated_values: Optional[Json] = None
|
||||||
|
|
||||||
|
|
||||||
class LiteLLM_SpendLogs_ResponseObject(LiteLLMBase):
|
class LiteLLM_SpendLogs_ResponseObject(LiteLLMBase):
|
||||||
response: Optional[List[Union[LiteLLM_SpendLogs, Any]]] = None
|
response: Optional[List[Union[LiteLLM_SpendLogs, Any]]] = None
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,5 @@ general_settings:
|
||||||
master_key: sk-1234
|
master_key: sk-1234
|
||||||
|
|
||||||
litellm_settings:
|
litellm_settings:
|
||||||
callbacks: ["otel"]
|
callbacks: ["otel"]
|
||||||
|
store_audit_logs: true
|
|
@ -10357,45 +10357,62 @@ async def new_team(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True
|
||||||
|
if litellm.store_audit_logs is True:
|
||||||
|
_updated_values = complete_team_data.json(exclude_none=True)
|
||||||
|
_updated_values = json.dumps(_updated_values)
|
||||||
|
|
||||||
|
asyncio.create_task(
|
||||||
|
create_audit_log_for_update(
|
||||||
|
request_data=LiteLLM_AuditLogs(
|
||||||
|
id=str(uuid.uuid4()),
|
||||||
|
updated_at=datetime.now(timezone.utc),
|
||||||
|
changed_by=user_api_key_dict.user_id or litellm_proxy_admin_name,
|
||||||
|
table_name=LitellmTableNames.TEAM_TABLE_NAME,
|
||||||
|
object_id=data.team_id,
|
||||||
|
action="created",
|
||||||
|
updated_values=_updated_values,
|
||||||
|
before_value=None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return team_row.model_dump()
|
return team_row.model_dump()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return team_row.dict()
|
return team_row.dict()
|
||||||
|
|
||||||
|
|
||||||
async def create_audit_log_for_update(
|
async def create_audit_log_for_update(request_data: LiteLLM_AuditLogs):
|
||||||
action: Literal["create", "update", "delete"],
|
if premium_user is not True:
|
||||||
# fyi: pylint does not directly allow you to pass Literal["LiteLLM_TeamTable"]
|
|
||||||
# because LiteLLM_TeamTable is also defined in _types.py
|
|
||||||
table_name: Literal[
|
|
||||||
LitellmTableNames.TEAM_TABLE_NAME,
|
|
||||||
LitellmTableNames.USER_TABLE_NAME,
|
|
||||||
LitellmTableNames.PROXY_MODEL_TABLE_NAME,
|
|
||||||
],
|
|
||||||
object_id: str,
|
|
||||||
changed_by: str,
|
|
||||||
before_value: dict,
|
|
||||||
after_value: dict,
|
|
||||||
):
|
|
||||||
if not premium_user:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
if litellm.store_audit_logs is not True:
|
||||||
pass
|
return
|
||||||
|
if prisma_client is None:
|
||||||
|
raise Exception("prisma_client is None, no DB connected")
|
||||||
|
|
||||||
|
verbose_proxy_logger.debug("creating audit log for %s", request_data)
|
||||||
|
|
||||||
|
if isinstance(request_data.updated_values, dict):
|
||||||
|
request_data.updated_values = json.dumps(request_data.updated_values)
|
||||||
|
|
||||||
|
if isinstance(request_data.before_value, dict):
|
||||||
|
request_data.before_value = json.dumps(request_data.before_value)
|
||||||
|
|
||||||
|
_request_data = request_data.dict(exclude_none=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await prisma_client.db.litellm_auditlog.create(
|
||||||
|
data={
|
||||||
|
**_request_data, # type: ignore
|
||||||
|
}
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# [Non-Blocking Exception. Do not allow blocking LLM API call]
|
# [Non-Blocking Exception. Do not allow blocking LLM API call]
|
||||||
verbose_proxy_logger.error(f"Failed Creating audit log {e}")
|
verbose_proxy_logger.error(f"Failed Creating audit log {e}")
|
||||||
|
|
||||||
# await prisma_client.create_audit_log(
|
|
||||||
# data={
|
|
||||||
# "action": action,
|
|
||||||
# "object_id": object_id,
|
|
||||||
# "changed_by": changed_by,
|
|
||||||
# "before_value": json.dumps(before_value),
|
|
||||||
# "after_value": json.dumps(after_value),
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@ -10471,7 +10488,28 @@ async def update_team(
|
||||||
team_id=data.team_id,
|
team_id=data.team_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
return team_row
|
# Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True
|
||||||
|
if litellm.store_audit_logs is True:
|
||||||
|
_before_value = existing_team_row.json(exclude_none=True)
|
||||||
|
_before_value = json.dumps(_before_value)
|
||||||
|
_after_value: str = json.dumps(updated_kv)
|
||||||
|
|
||||||
|
asyncio.create_task(
|
||||||
|
create_audit_log_for_update(
|
||||||
|
request_data=LiteLLM_AuditLogs(
|
||||||
|
id=str(uuid.uuid4()),
|
||||||
|
updated_at=datetime.now(timezone.utc),
|
||||||
|
changed_by=user_api_key_dict.user_id or litellm_proxy_admin_name,
|
||||||
|
table_name=LitellmTableNames.TEAM_TABLE_NAME,
|
||||||
|
object_id=data.team_id,
|
||||||
|
action="updated",
|
||||||
|
updated_values=_after_value,
|
||||||
|
before_value=_before_value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return team_row
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
|
@ -10742,6 +10780,35 @@ async def delete_team(
|
||||||
detail={"error": f"Team not found, passed team_id={team_id}"},
|
detail={"error": f"Team not found, passed team_id={team_id}"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True
|
||||||
|
# we do this after the first for loop, since first for loop is for validation. we only want this inserted after validation passes
|
||||||
|
if litellm.store_audit_logs is True:
|
||||||
|
# make an audit log for each team deleted
|
||||||
|
for team_id in data.team_ids:
|
||||||
|
team_row = await prisma_client.get_data( # type: ignore
|
||||||
|
team_id=team_id, table_name="team", query_type="find_unique"
|
||||||
|
)
|
||||||
|
|
||||||
|
_team_row = team_row.json(exclude_none=True)
|
||||||
|
|
||||||
|
asyncio.create_task(
|
||||||
|
create_audit_log_for_update(
|
||||||
|
request_data=LiteLLM_AuditLogs(
|
||||||
|
id=str(uuid.uuid4()),
|
||||||
|
updated_at=datetime.now(timezone.utc),
|
||||||
|
changed_by=user_api_key_dict.user_id
|
||||||
|
or litellm_proxy_admin_name,
|
||||||
|
table_name=LitellmTableNames.TEAM_TABLE_NAME,
|
||||||
|
object_id=team_id,
|
||||||
|
action="deleted",
|
||||||
|
updated_values="{}",
|
||||||
|
before_value=_team_row,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# End of Audit logging
|
||||||
|
|
||||||
## DELETE ASSOCIATED KEYS
|
## DELETE ASSOCIATED KEYS
|
||||||
await prisma_client.delete_data(team_id_list=data.team_ids, table_name="key")
|
await prisma_client.delete_data(team_id_list=data.team_ids, table_name="key")
|
||||||
## DELETE TEAMS
|
## DELETE TEAMS
|
||||||
|
|
|
@ -243,4 +243,16 @@ model LiteLLM_InvitationLink {
|
||||||
liteLLM_user_table_user LiteLLM_UserTable @relation("UserId", fields: [user_id], references: [user_id])
|
liteLLM_user_table_user LiteLLM_UserTable @relation("UserId", fields: [user_id], references: [user_id])
|
||||||
liteLLM_user_table_created LiteLLM_UserTable @relation("CreatedBy", fields: [created_by], references: [user_id])
|
liteLLM_user_table_created LiteLLM_UserTable @relation("CreatedBy", fields: [created_by], references: [user_id])
|
||||||
liteLLM_user_table_updated LiteLLM_UserTable @relation("UpdatedBy", fields: [updated_by], references: [user_id])
|
liteLLM_user_table_updated LiteLLM_UserTable @relation("UpdatedBy", fields: [updated_by], references: [user_id])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
model LiteLLM_AuditLog {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
updated_at DateTime @default(now())
|
||||||
|
changed_by String // user or system that performed the action
|
||||||
|
action String // create, update, delete
|
||||||
|
table_name String // on of LitellmTableNames.TEAM_TABLE_NAME, LitellmTableNames.USER_TABLE_NAME, LitellmTableNames.PROXY_MODEL_TABLE_NAME,
|
||||||
|
object_id String // id of the object being audited. This can be the key id, team id, user id, model id
|
||||||
|
before_value Json? // value of the row
|
||||||
|
updated_values Json? // value of the row after change
|
||||||
}
|
}
|
|
@ -246,13 +246,13 @@ model LiteLLM_InvitationLink {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
model AuditLog {
|
model LiteLLM_AuditLog {
|
||||||
id Int @id @default(autoincrement())
|
id String @id @default(uuid())
|
||||||
updated_at DateTime @default(now())
|
updated_at DateTime @default(now())
|
||||||
changed_by String // user or system that performed the action
|
changed_by String // user or system that performed the action
|
||||||
action String // create, update, delete
|
action String // create, update, delete
|
||||||
object_type String // team, key, user, model
|
table_name String // on of LitellmTableNames.TEAM_TABLE_NAME, LitellmTableNames.USER_TABLE_NAME, LitellmTableNames.PROXY_MODEL_TABLE_NAME,
|
||||||
object_id String // id of the object being audited. This can be the key id, team id, user id, model id
|
object_id String // id of the object being audited. This can be the key id, team id, user id, model id
|
||||||
before_value Json? // value of the row
|
before_value Json? // value of the row
|
||||||
after_value Json? // value of the row after change
|
updated_values Json? // value of the row after change
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue