157 lines
5.6 KiB
Python
157 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
import scripts.compare_1c_access_role_audit as compare_script
|
|
import scripts.export_1c_access_role_audit as export_script
|
|
|
|
|
|
def compare_stem(old_path: Path, new_path: Path) -> str:
|
|
return f"compare-{old_path.stem.replace('.summary', '')}-{new_path.stem.replace('.summary', '')}"
|
|
|
|
|
|
def verdict_for(export_summary: dict[str, Any], compare_result: dict[str, Any] | None) -> str:
|
|
counts = compare_result.get("counts") if isinstance(compare_result, dict) and isinstance(compare_result.get("counts"), dict) else {}
|
|
if any(int(counts.get(key) or 0) > 0 for key in ("added_users", "removed_users", "changed_access_paths")):
|
|
return "changed"
|
|
if str(export_summary.get("risk_level") or "").lower() in {"medium", "high"}:
|
|
return "risk"
|
|
return "ok"
|
|
|
|
|
|
def audit_role(
|
|
*,
|
|
adapter_url: str,
|
|
base_id: str,
|
|
role: str,
|
|
report_root: Path,
|
|
timeout: int,
|
|
user_threshold: int,
|
|
limit: int,
|
|
include_html: bool = True,
|
|
) -> dict[str, Any]:
|
|
export_summary = export_script.export_role_audit(
|
|
adapter_url=adapter_url,
|
|
base_id=base_id,
|
|
role=role,
|
|
report_root=report_root,
|
|
timeout=timeout,
|
|
user_threshold=user_threshold,
|
|
include_analysis=True,
|
|
limit=limit,
|
|
include_html=include_html,
|
|
)
|
|
latest = compare_script.find_latest_summaries(report_root, base_id, role=role, count=2)
|
|
compare_result: dict[str, Any] | None = None
|
|
if len(latest) >= 2:
|
|
new_path, old_path = latest[0], latest[1]
|
|
folder = report_root / compare_script.slugify(base_id, max_length=60)
|
|
stem = compare_stem(old_path, new_path)
|
|
compare_result = compare_script.compare_files(
|
|
old_path,
|
|
new_path,
|
|
output=folder / f"{stem}.json",
|
|
html_output=folder / f"{stem}.html",
|
|
)
|
|
export_script.update_index(report_root, base_id)
|
|
return {
|
|
"schema": "onec_access_role_audit_run.v1",
|
|
"status": export_summary.get("status"),
|
|
"base_id": base_id,
|
|
"role": role,
|
|
"verdict": verdict_for(export_summary, compare_result),
|
|
"export": export_summary,
|
|
"compare": compare_result,
|
|
}
|
|
|
|
|
|
def audit_config(
|
|
*,
|
|
config_path: Path,
|
|
adapter_url: str,
|
|
report_root: Path,
|
|
timeout: int,
|
|
limit: int,
|
|
include_html: bool = True,
|
|
) -> dict[str, Any]:
|
|
config = json.loads(config_path.read_text(encoding="utf-8"))
|
|
roles = config.get("roles") if isinstance(config.get("roles"), list) else []
|
|
default_base_id = str(config.get("base_id") or "upo_test")
|
|
results: list[dict[str, Any]] = []
|
|
for item in roles:
|
|
if not isinstance(item, dict) or not item.get("role"):
|
|
continue
|
|
results.append(
|
|
audit_role(
|
|
adapter_url=adapter_url,
|
|
base_id=str(item.get("base_id") or default_base_id),
|
|
role=str(item.get("role")),
|
|
report_root=report_root,
|
|
timeout=timeout,
|
|
user_threshold=int(item.get("user_threshold") or 50),
|
|
limit=limit,
|
|
include_html=include_html,
|
|
)
|
|
)
|
|
verdicts = [str(item.get("verdict") or "") for item in results]
|
|
overall = "changed" if "changed" in verdicts else "risk" if "risk" in verdicts else "ok"
|
|
return {
|
|
"schema": "onec_access_role_audit_batch.v1",
|
|
"status": "ok" if all(item.get("status") == "ok" for item in results) else "error",
|
|
"config": str(config_path),
|
|
"overall_verdict": overall,
|
|
"counts": {"roles": len(results), "changed": verdicts.count("changed"), "risk": verdicts.count("risk"), "ok": verdicts.count("ok")},
|
|
"results": results,
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Run 1C role access audit: export, analyze, update index, compare with previous.")
|
|
parser.add_argument("--adapter-url", default=export_script.DEFAULT_BASE_URL)
|
|
parser.add_argument("--base-id", default="upo_test")
|
|
parser.add_argument("--role")
|
|
parser.add_argument("--config", type=Path, help="Run all roles from an access critical roles JSON config.")
|
|
parser.add_argument("--report-root", type=Path, default=export_script.DEFAULT_REPORT_ROOT)
|
|
parser.add_argument("--timeout", type=int, default=120)
|
|
parser.add_argument("--limit", type=int, default=20000)
|
|
parser.add_argument("--user-threshold", type=int, default=50)
|
|
parser.add_argument("--no-html", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
if args.config:
|
|
result = audit_config(
|
|
config_path=args.config,
|
|
adapter_url=args.adapter_url,
|
|
report_root=args.report_root,
|
|
timeout=args.timeout,
|
|
limit=args.limit,
|
|
include_html=not args.no_html,
|
|
)
|
|
else:
|
|
if not args.role:
|
|
parser.error("--role is required unless --config is used.")
|
|
result = audit_role(
|
|
adapter_url=args.adapter_url,
|
|
base_id=args.base_id,
|
|
role=args.role,
|
|
report_root=args.report_root,
|
|
timeout=args.timeout,
|
|
user_threshold=args.user_threshold,
|
|
limit=args.limit,
|
|
include_html=not args.no_html,
|
|
)
|
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
return 0 if result.get("status") == "ok" else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|