First commit
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
"""
|
||||
Rules.py
|
||||
Contiene los endpoints de CRUD de reglas.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from sqlalchemy.orm import Session
|
||||
from database import get_db
|
||||
import models
|
||||
import schemas
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
from auth import get_current_user
|
||||
from audit import log_action
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/rules/", response_model=schemas.RuleResponse, tags=['Rules'])
|
||||
def create_rule(
|
||||
rule: schemas.RuleCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: int = Depends(get_current_user)
|
||||
):
|
||||
# Validar que la regex compile antes de guardar
|
||||
try:
|
||||
re.compile(rule.regex)
|
||||
except re.error as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Expresión regular inválida: {e}"
|
||||
)
|
||||
|
||||
db_rule = models.Rule(**rule.model_dump(exclude={"apply_to_history"}))
|
||||
db.add(db_rule)
|
||||
db.flush() # para tener db_rule.id antes del commit
|
||||
|
||||
# Auditoría
|
||||
log_action(
|
||||
db=db, entity_type='rule', entity_id=db_rule.id,
|
||||
action='create', user_id=current_user,
|
||||
after=db_rule, ip_address=request.client.host
|
||||
)
|
||||
|
||||
|
||||
if rule.apply_to_history:
|
||||
# Traer mensajes en batches para no cargar todo en memoria
|
||||
batch_size = 500
|
||||
offset = 0
|
||||
while True:
|
||||
messages = db.query(models.Message).offset(offset).limit(batch_size).all()
|
||||
if not messages:
|
||||
break
|
||||
for message in messages:
|
||||
try:
|
||||
if re.search(rule.regex, message.content, re.IGNORECASE):
|
||||
new_alert = models.Alert(
|
||||
message_id=message.id_mess_g,
|
||||
group_id=message.group_id,
|
||||
rule_id=db_rule.id,
|
||||
status="open",
|
||||
notes=f"Detected pattern: {rule.regex}",
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
db.add(new_alert)
|
||||
except re.error:
|
||||
break
|
||||
offset += batch_size
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_rule)
|
||||
return db_rule
|
||||
|
||||
@router.get("/rules/", response_model=List[schemas.RuleResponse], tags=['Rules'])
|
||||
def read_rules(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: int = Depends(get_current_user)
|
||||
):
|
||||
return db.query(models.Rule).offset(skip).limit(limit).all()
|
||||
|
||||
@router.get("/rules/search/", response_model=List[schemas.RuleResponse], tags=['Rules'])
|
||||
def search_rules(
|
||||
q: str,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: int = Depends(get_current_user)
|
||||
):
|
||||
return db.query(models.Rule).filter(
|
||||
models.Rule.description.ilike(f"%{q}%")
|
||||
).offset(skip).limit(limit).all()
|
||||
|
||||
@router.get("/rules/{rule_id}", response_model=schemas.RuleResponse, tags=['Rules'])
|
||||
def read_rule(
|
||||
rule_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: int = Depends(get_current_user)
|
||||
):
|
||||
db_rule = db.query(models.Rule).filter(models.Rule.id == rule_id).first()
|
||||
if not db_rule:
|
||||
raise HTTPException(status_code=404, detail="Rule not found")
|
||||
return db_rule
|
||||
|
||||
@router.put("/rules/{rule_id}", response_model=schemas.RuleResponse, tags=['Rules'])
|
||||
def update_rule(
|
||||
rule_id: int,
|
||||
rule: schemas.RuleCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: int = Depends(get_current_user)
|
||||
):
|
||||
db_rule = db.query(models.Rule).filter(models.Rule.id == rule_id).first()
|
||||
if not db_rule:
|
||||
raise HTTPException(status_code=404, detail="Rule not found")
|
||||
|
||||
# Capturar estado anterior antes de modificar
|
||||
before_snapshot = {
|
||||
'id': db_rule.id,
|
||||
'description': db_rule.description,
|
||||
'regex': db_rule.regex,
|
||||
'severity': db_rule.severity,
|
||||
'is_active': db_rule.is_active,
|
||||
}
|
||||
|
||||
for field, value in rule.model_dump().items():
|
||||
setattr(db_rule, field, value)
|
||||
|
||||
log_action(
|
||||
db=db, entity_type='rule', entity_id=rule_id,
|
||||
action='update', user_id=current_user,
|
||||
before=before_snapshot, after=db_rule,
|
||||
ip_address=request.client.host
|
||||
)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_rule)
|
||||
return db_rule
|
||||
|
||||
@router.delete("/rules/{rule_id}", tags=['Rules'])
|
||||
def delete_rule(
|
||||
rule_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: int = Depends(get_current_user)
|
||||
):
|
||||
db_rule = db.query(models.Rule).filter(models.Rule.id == rule_id).first()
|
||||
if not db_rule:
|
||||
raise HTTPException(status_code=404, detail="Rule not found")
|
||||
|
||||
log_action(
|
||||
db=db, entity_type='rule', entity_id=rule_id,
|
||||
action='delete', user_id=current_user,
|
||||
before=db_rule, ip_address=request.client.host
|
||||
)
|
||||
|
||||
db.delete(db_rule)
|
||||
db.commit()
|
||||
return {"message": "Rule deleted successfully"}
|
||||
Reference in New Issue
Block a user