feat(bitdefender): complete Companies module (build-out 2/N)

- Completed Companies module for bitdefender GravityZone Public API
- Implemented: getCompanyDetails, getCompanyDetailsByUser, createCompany, suspendCompany, activateCompany, deleteCompany
- Discovered updateCompany and getCompaniesList not available; companies retrieved via network inventory
- Company types: 0=Partner, 1=Customer; createCompany accepts nested licenseSubscription via JSON passthrough
- All write operations require --confirm; raw also restricts createCompany/suspendCompany/activateCompany
- selftest 49 -> 55 passing

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 10:26:14 -07:00
parent 8a64bc48e6
commit 2a1ffab19f
5 changed files with 168 additions and 8 deletions

View File

@@ -211,6 +211,51 @@ def cmd_companies(client, args):
_emit(client.list_companies(), args.json, _print_company_table)
def cmd_company(client, args):
_emit(client.get_company_details(args.company_id), args.json, _print_kv)
def cmd_company_by_user(client, args):
_emit(client.get_company_by_user(args.username), args.json, _print_kv)
def cmd_company_create(client, args):
extra, rc = _load_json_arg(args.extra_json, "extra-json")
if rc:
return rc
label = {0: "Partner", 1: "Customer"}.get(args.type, str(args.type))
if not _gated(f"create {label} company '{args.name}'", args.confirm):
return 3
result = client.create_company(args.type, args.name, parent_id=args.parent,
extra=extra or None)
_emit({"createdCompany": args.name, "result": result}, args.json, _print_kv)
return 0
def cmd_company_suspend(client, args):
if not _gated(f"suspend company {args.id}", args.confirm):
return 3
_emit({"suspended": args.id, "result": client.suspend_company(args.id)},
args.json, _print_kv)
return 0
def cmd_company_activate(client, args):
if not _gated(f"activate company {args.id}", args.confirm):
return 3
_emit({"activated": args.id, "result": client.activate_company(args.id)},
args.json, _print_kv)
return 0
def cmd_company_delete(client, args):
if not _gated(f"delete company {args.id}", args.confirm):
return 3
_emit({"deletedCompany": args.id, "result": client.delete_company(args.id)},
args.json, _print_kv)
return 0
def cmd_endpoints(client, args):
_emit(client.list_endpoints(args.company, per_page=args.per_page),
args.json, _print_endpoint_table)
@@ -478,7 +523,8 @@ DESTRUCTIVE_RAW_PATTERNS = ("delete", "createuninstall", "createremove",
"createreconfigure", "isolat", "addtoblocklist",
"removefromblocklist", "assignpolicy",
"setpushevent", "createaccount", "updateaccount",
"configurenotif")
"configurenotif", "createcompany", "suspendcompany",
"activatecompany")
def _is_destructive_method(method: str) -> bool:
@@ -596,6 +642,40 @@ def build_parser() -> argparse.ArgumentParser:
sub.add_parser("status", help="API key + license status.", parents=[common])
sub.add_parser("companies", help="List client companies.", parents=[common])
sp = sub.add_parser("company", help="Company detail (no id = own company).",
parents=[common])
sp.add_argument("company_id", nargs="?", help="Company id (optional).")
sp = sub.add_parser("company-by-user", help="Company that owns a username.",
parents=[common])
sp.add_argument("--username", required=True)
sp = sub.add_parser("company-create", help="Create a company (gated).",
parents=[common])
sp.add_argument("--type", type=int, required=True,
help="0=Partner, 1=Customer.")
sp.add_argument("--name", required=True)
sp.add_argument("--parent", help="parentId (defaults to the key's company).")
sp.add_argument("--extra-json",
help="JSON object of extra fields (licenseSubscription, "
"address, assignedProductType, ...).")
sp.add_argument("--confirm", action="store_true")
sp = sub.add_parser("company-suspend", help="Suspend a company (gated).",
parents=[common])
sp.add_argument("--id", required=True)
sp.add_argument("--confirm", action="store_true")
sp = sub.add_parser("company-activate", help="Activate a company (gated).",
parents=[common])
sp.add_argument("--id", required=True)
sp.add_argument("--confirm", action="store_true")
sp = sub.add_parser("company-delete", help="Delete a company (gated).",
parents=[common])
sp.add_argument("--id", required=True)
sp.add_argument("--confirm", action="store_true")
sp = sub.add_parser("endpoints", help="List endpoints under a company/group.",
parents=[common])
sp.add_argument("--company", help="Parent company/group id.")
@@ -814,6 +894,12 @@ def build_parser() -> argparse.ArgumentParser:
HANDLERS = {
"status": cmd_status,
"companies": cmd_companies,
"company": cmd_company,
"company-by-user": cmd_company_by_user,
"company-create": cmd_company_create,
"company-suspend": cmd_company_suspend,
"company-activate": cmd_company_activate,
"company-delete": cmd_company_delete,
"endpoints": cmd_endpoints,
"endpoint": cmd_endpoint,
"sweep": cmd_sweep,