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:
@@ -22,10 +22,13 @@ Per-module workflow: fetch module doc -> list methods + params -> add to
|
||||
- [x] getLicenseInfo
|
||||
- [ ] (enumerate: getMonthlyUsage? / others)
|
||||
|
||||
## companies
|
||||
- [x] getCompanyDetails
|
||||
- [ ] getCompaniesList / getCompanyDetailsByUser
|
||||
- [ ] createCompany / updateCompany / deleteCompany / suspend / activate (enumerate; gated)
|
||||
## companies — COMPLETE (6; no updateCompany/getCompaniesList — those don't exist)
|
||||
- [x] getCompanyDetails (no id = own)
|
||||
- [x] getCompanyDetailsByUser (username)
|
||||
- [x] createCompany (gated; type 0=Partner/1=Customer + name)
|
||||
- [x] suspendCompany (gated)
|
||||
- [x] activateCompany (gated)
|
||||
- [x] deleteCompany (gated)
|
||||
|
||||
## network
|
||||
- [x] getNetworkInventoryItems · getEndpointsList · getManagedEndpointDetails
|
||||
|
||||
@@ -66,12 +66,20 @@ In `getNetworkInventoryItems` results, `type == 1` denotes a company node.
|
||||
|---|---|---|---|
|
||||
| `getLicenseInfo` | `{}` | VERIFIED | Seats, expiry, usage. |
|
||||
|
||||
## companies (`/companies`)
|
||||
## companies (`/companies`) — COMPLETE (6 methods; no updateCompany/getCompaniesList)
|
||||
|
||||
> `updateCompany` and `getCompaniesList` return "method not found" — they do NOT
|
||||
> exist. Enumerate companies via `network.getNetworkInventoryItems` (the `companies`
|
||||
> CLI cmd). `type`: 0=Partner, 1=Customer.
|
||||
|
||||
| Method | Params | Status | Notes |
|
||||
|---|---|---|---|
|
||||
| `getCompanyDetails` | `{}` or `{companyId}` | VERIFIED | Own company when no arg; a specific company when `companyId` given. |
|
||||
| `getCompanyDetailsByUser` | uncertain | UNVERIFIED | Param shape not confirmed. Use `raw` if needed. |
|
||||
| `getCompanyDetails` | `{}` or `{companyId}` | VERIFIED LIVE | Own company when no arg. CLI `company [id]`. |
|
||||
| `getCompanyDetailsByUser` | `username` | VERIFIED LIVE | Company that owns a user. CLI `company-by-user`. |
|
||||
| `createCompany` | `type (req), name (req), parentId?, address?, country?, state?, phone?, industry?, canBeManagedByAbove?, assignedProductType?, licenseSubscription{type,reservedSlots,endSubscription,autoRenewPeriod,...}` | VERIFIED (docs + probe) | CLI `company-create`, gated. STATE-CHANGING. Docs: 77211-126236-createcompany.html |
|
||||
| `suspendCompany` | `companyId` | VERIFIED (probe) | CLI `company-suspend`, gated. STATE-CHANGING. |
|
||||
| `activateCompany` | `companyId` | VERIFIED (probe) | CLI `company-activate`, gated. STATE-CHANGING. |
|
||||
| `deleteCompany` | `companyId` | VERIFIED (probe) | CLI `company-delete`, gated. STATE-CHANGING. |
|
||||
|
||||
## network (`/network`)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -254,6 +254,61 @@ class GravityZoneClient:
|
||||
def get_own_company(self) -> dict:
|
||||
return self._jsonrpc_request("companies", "getCompanyDetails", {}) or {}
|
||||
|
||||
def get_company_details(self, company_id: Optional[str] = None) -> dict:
|
||||
"""Company detail (companies.getCompanyDetails). No id ⇒ own company."""
|
||||
params: dict = {}
|
||||
if company_id:
|
||||
params["companyId"] = company_id
|
||||
return self._jsonrpc_request("companies", "getCompanyDetails", params) or {}
|
||||
|
||||
def get_company_by_user(self, username: str) -> dict:
|
||||
"""Company that owns a given user (companies.getCompanyDetailsByUser)."""
|
||||
return self._jsonrpc_request(
|
||||
"companies", "getCompanyDetailsByUser", {"username": username}
|
||||
) or {}
|
||||
|
||||
def create_company(
|
||||
self,
|
||||
company_type: int,
|
||||
name: str,
|
||||
parent_id: Optional[str] = None,
|
||||
extra: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""Create a company (companies.createCompany). STATE-CHANGING.
|
||||
|
||||
Required (verified): `type` (0=Partner, 1=Customer) and `name`. Documented
|
||||
optional params: parentId, address, country, state, phone, industry,
|
||||
canBeManagedByAbove, assignedProductType, and a nested `licenseSubscription`
|
||||
{type (3=monthly subscription, 4=monthly trial), reservedSlots,
|
||||
endSubscription, autoRenewPeriod, ...}. Pass those via `extra`. Gate at
|
||||
the call site behind --confirm.
|
||||
Docs: bitdefender.com/business/support/en/77211-126236-createcompany.html
|
||||
"""
|
||||
params: dict = {"type": company_type, "name": name}
|
||||
if parent_id is not None:
|
||||
params["parentId"] = parent_id
|
||||
if extra:
|
||||
params.update(extra)
|
||||
return self._jsonrpc_request("companies", "createCompany", params)
|
||||
|
||||
def suspend_company(self, company_id: str) -> Any:
|
||||
"""Suspend a company (companies.suspendCompany). STATE-CHANGING. Gated."""
|
||||
return self._jsonrpc_request(
|
||||
"companies", "suspendCompany", {"companyId": company_id}
|
||||
)
|
||||
|
||||
def activate_company(self, company_id: str) -> Any:
|
||||
"""Activate a suspended company (companies.activateCompany). STATE-CHANGING."""
|
||||
return self._jsonrpc_request(
|
||||
"companies", "activateCompany", {"companyId": company_id}
|
||||
)
|
||||
|
||||
def delete_company(self, company_id: str) -> Any:
|
||||
"""Delete a company (companies.deleteCompany). STATE-CHANGING. Gated."""
|
||||
return self._jsonrpc_request(
|
||||
"companies", "deleteCompany", {"companyId": company_id}
|
||||
)
|
||||
|
||||
def list_companies(self, page: int = 1, per_page: int = 100) -> dict:
|
||||
result = self._jsonrpc_request(
|
||||
"network",
|
||||
|
||||
@@ -105,6 +105,14 @@ check("push-set no confirm -> rc3", ["push-set", "--status", "1", "--url", "http
|
||||
check("push-set enable no url -> rc2", ["push-set", "--status", "1", "--confirm"], want_rc=2)
|
||||
check("raw assignPolicy no confirm -> rc3", ["raw", "--module", "network", "--method", "assignPolicy", "--params", "{}"], want_rc=3)
|
||||
|
||||
# --- companies module ---
|
||||
check("company (own, no id)", ["company"], want_rc=0)
|
||||
check("company-create no confirm -> rc3", ["company-create", "--type", "1", "--name", "Test Co"], want_rc=3, out_has="Would")
|
||||
check("company-suspend no confirm -> rc3", ["company-suspend", "--id", "x"], want_rc=3)
|
||||
check("company-activate no confirm -> rc3", ["company-activate", "--id", "x"], want_rc=3)
|
||||
check("company-delete no confirm -> rc3", ["company-delete", "--id", "x"], want_rc=3)
|
||||
check("raw createCompany no confirm -> rc3", ["raw", "--module", "companies", "--method", "createCompany", "--params", "{}"], want_rc=3)
|
||||
|
||||
# --- accounts module ---
|
||||
check("account (own, no id)", ["account"], want_rc=0)
|
||||
check("account-create no confirm -> rc3", ["account-create", "--email", "t@x.io"], want_rc=3, out_has="Would")
|
||||
|
||||
Reference in New Issue
Block a user