9883e6aab3
Allow host-level login/password for home/workgroup PCs while keeping global domain admins in Settings.
110 lines
3.2 KiB
Python
110 lines
3.2 KiB
Python
"""Queue and resolve agent commands (legacy poll path + helpers)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models import AgentCommand, Host
|
|
from app.services.win_admin_settings import (
|
|
get_effective_win_admin_config,
|
|
get_effective_win_admin_for_host,
|
|
)
|
|
|
|
|
|
def _win_admin_configured() -> bool:
|
|
return get_effective_win_admin_config().configured
|
|
|
|
|
|
def win_admin_run_as(db: Session | None = None, host: Host | None = None) -> dict[str, str] | None:
|
|
if host is not None and db is not None:
|
|
cfg = get_effective_win_admin_for_host(db, host)
|
|
else:
|
|
cfg = get_effective_win_admin_config(db)
|
|
if not cfg.configured:
|
|
return None
|
|
return {
|
|
"user": cfg.user,
|
|
"password": cfg.password,
|
|
}
|
|
|
|
|
|
def command_to_dict(
|
|
cmd: AgentCommand,
|
|
*,
|
|
include_run_as: bool = False,
|
|
db: Session | None = None,
|
|
host: Host | None = None,
|
|
) -> dict:
|
|
out = {
|
|
"id": cmd.command_uuid,
|
|
"type": cmd.command_type,
|
|
"params": cmd.params if isinstance(cmd.params, dict) else {},
|
|
"status": cmd.status,
|
|
"result_stdout": cmd.result_stdout,
|
|
"result_stderr": cmd.result_stderr,
|
|
"created_at": cmd.created_at.isoformat() if cmd.created_at else None,
|
|
"completed_at": cmd.completed_at.isoformat() if cmd.completed_at else None,
|
|
}
|
|
if include_run_as and cmd.status == "pending":
|
|
run_as = win_admin_run_as(db, host)
|
|
if run_as:
|
|
out["run_as"] = run_as
|
|
return out
|
|
|
|
|
|
def command_response_fields(cmd: AgentCommand) -> dict:
|
|
params = cmd.params if isinstance(cmd.params, dict) else {}
|
|
return {
|
|
"target": params.get("winrm_target") or params.get("client_hostname"),
|
|
"client_hostname": params.get("client_hostname"),
|
|
"internal_ip": params.get("internal_ip"),
|
|
}
|
|
|
|
|
|
def get_command_for_host_poll(db: Session, host: Host, *, limit: int = 5) -> list[AgentCommand]:
|
|
return list(
|
|
db.scalars(
|
|
select(AgentCommand)
|
|
.where(
|
|
AgentCommand.host_id == host.id,
|
|
AgentCommand.status == "pending",
|
|
)
|
|
.order_by(AgentCommand.created_at.asc())
|
|
.limit(limit)
|
|
).all()
|
|
)
|
|
|
|
|
|
def resolve_host_by_agent_instance_id(db: Session, agent_instance_id: str) -> Host | None:
|
|
if not agent_instance_id.strip():
|
|
return None
|
|
return db.scalar(select(Host).where(Host.agent_instance_id == agent_instance_id.strip()))
|
|
|
|
|
|
def complete_command(
|
|
db: Session,
|
|
command_uuid: str,
|
|
*,
|
|
status: str,
|
|
stdout: str | None = None,
|
|
stderr: str | None = None,
|
|
) -> AgentCommand | None:
|
|
cmd = db.scalar(select(AgentCommand).where(AgentCommand.command_uuid == command_uuid))
|
|
if cmd is None:
|
|
return None
|
|
if cmd.status != "pending":
|
|
return cmd
|
|
cmd.status = status
|
|
cmd.result_stdout = stdout
|
|
cmd.result_stderr = stderr
|
|
cmd.completed_at = datetime.now(timezone.utc)
|
|
db.flush()
|
|
return cmd
|
|
|
|
|
|
def get_command_by_uuid(db: Session, command_uuid: str) -> AgentCommand | None:
|
|
return db.scalar(select(AgentCommand).where(AgentCommand.command_uuid == command_uuid))
|