Compare commits
3 Commits
9f88891457
...
d5b7e38dd7
| Author | SHA1 | Date | |
|---|---|---|---|
| d5b7e38dd7 | |||
| 6de01f85bb | |||
| 86e7f11227 |
@@ -0,0 +1,101 @@
|
||||
id: infoblox
|
||||
name: Infoblox NIOS
|
||||
version: 1.0.0
|
||||
description: "Infoblox NIOS (WAPI) — DNS-layer containment and DDI lookups: block a domain via an RPZ record (DNS sinkhole), list and delete RPZ records, search host records, look up networks, and add A records. HTTP Basic authentication; stdlib-only, no extra Python dependencies."
|
||||
changelog: "1.0.0 — Initial release: add/list/delete RPZ records, search hosts, get networks, add A record."
|
||||
category: network
|
||||
|
||||
# Per-instance configuration. HTTP Basic auth against the Grid Master WAPI.
|
||||
config_schema:
|
||||
properties:
|
||||
base_url:
|
||||
type: string
|
||||
description: "Grid Master URL (e.g. https://infoblox.example.com)"
|
||||
username:
|
||||
type: string
|
||||
description: "WAPI username"
|
||||
password:
|
||||
type: string
|
||||
description: "WAPI password"
|
||||
x-soar-sensitive: true
|
||||
wapi_version:
|
||||
type: string
|
||||
description: "WAPI version (default v2.12)"
|
||||
default: "v2.12"
|
||||
insecure:
|
||||
type: boolean
|
||||
description: "Trust any TLS certificate (not secure)"
|
||||
default: false
|
||||
required:
|
||||
- base_url
|
||||
- username
|
||||
- password
|
||||
|
||||
commands:
|
||||
- id: add_rpz_domain
|
||||
name: infoblox-add-rpz-domain
|
||||
description: "Block a domain by adding an RPZ CNAME record (returns NXDOMAIN — DNS sinkhole)."
|
||||
inputs_schema:
|
||||
properties:
|
||||
domain: { type: string, description: "Domain to block (e.g. bad.example.com)" }
|
||||
rpz_zone: { type: string, description: "RPZ response-policy zone name (e.g. rpz.local)" }
|
||||
view: { type: string, description: "DNS view (default 'default')" }
|
||||
required: [domain, rpz_zone]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: list_rpz_records
|
||||
name: infoblox-list-rpz-records
|
||||
description: "List RPZ CNAME records (optionally in a specific zone)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
rpz_zone: { type: string, description: "Optional RPZ zone filter" }
|
||||
max_results: { type: number, description: "Max records (default 100)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: delete_rpz_record
|
||||
name: infoblox-delete-rpz-record
|
||||
description: "Delete an RPZ record (unblock) by its object reference."
|
||||
inputs_schema:
|
||||
properties:
|
||||
ref: { type: string, description: "WAPI object reference (_ref) of the record" }
|
||||
required: [ref]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: search_host
|
||||
name: infoblox-search-host
|
||||
description: "Search host records by name (substring match)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
name: { type: string, description: "Host name (or partial)" }
|
||||
max_results: { type: number, description: "Max records (default 100)" }
|
||||
required: [name]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: get_network
|
||||
name: infoblox-get-network
|
||||
description: "Look up networks (optionally filtered by CIDR)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
network: { type: string, description: "Optional network CIDR filter (e.g. 10.0.0.0/24)" }
|
||||
max_results: { type: number, description: "Max networks (default 100)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: add_a_record
|
||||
name: infoblox-add-a-record
|
||||
description: "Add a DNS A record."
|
||||
inputs_schema:
|
||||
properties:
|
||||
name: { type: string, description: "FQDN of the record" }
|
||||
ipv4addr: { type: string, description: "IPv4 address" }
|
||||
view: { type: string, description: "DNS view (default 'default')" }
|
||||
required: [name, ipv4addr]
|
||||
outputs_schema: { properties: {} }
|
||||
|
||||
- id: test_connection
|
||||
name: infoblox-test-connection
|
||||
description: "Verify connectivity and credentials (used by the Test button)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
@@ -0,0 +1,78 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
name = inputs.get("name")
|
||||
ipv4addr = inputs.get("ipv4addr")
|
||||
if not name:
|
||||
raise Exception("name is required")
|
||||
if not ipv4addr:
|
||||
raise Exception("ipv4addr is required")
|
||||
view = inputs.get("view") or "default"
|
||||
|
||||
body = {
|
||||
"name": name,
|
||||
"ipv4addr": ipv4addr,
|
||||
"view": view,
|
||||
}
|
||||
resp = request("POST", "/record:a", cfg, body=body)
|
||||
return {"result": resp}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,79 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
domain = inputs.get("domain")
|
||||
rpz_zone = inputs.get("rpz_zone")
|
||||
if not domain:
|
||||
raise Exception("domain is required")
|
||||
if not rpz_zone:
|
||||
raise Exception("rpz_zone is required")
|
||||
view = inputs.get("view") or "default"
|
||||
|
||||
body = {
|
||||
"name": str(domain) + "." + str(rpz_zone),
|
||||
"canonical": "",
|
||||
"rp_zone": rpz_zone,
|
||||
"view": view,
|
||||
}
|
||||
resp = request("POST", "/record:rpz:cname", cfg, body=body)
|
||||
return {"result": resp}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,72 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
q = lambda v: urllib.parse.quote(str(v), safe="")
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
ref = inputs.get("ref")
|
||||
if not ref:
|
||||
raise Exception("ref is required")
|
||||
|
||||
resp = request("DELETE", "/" + q(ref), cfg)
|
||||
return {"deleted": resp}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,74 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
network = inputs.get("network")
|
||||
max_results = inputs.get("max_results")
|
||||
|
||||
params = {
|
||||
"_max_results": int(max_results or 100),
|
||||
}
|
||||
if network:
|
||||
params["network"] = network
|
||||
|
||||
resp = request("GET", "/network", cfg, params=params)
|
||||
return {"networks": resp}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,75 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
rpz_zone = inputs.get("rpz_zone")
|
||||
max_results = inputs.get("max_results")
|
||||
|
||||
params = {
|
||||
"_max_results": int(max_results or 100),
|
||||
"_return_fields+": "name,canonical,rp_zone,view",
|
||||
}
|
||||
if rpz_zone:
|
||||
params["rp_zone"] = rpz_zone
|
||||
|
||||
resp = request("GET", "/record:rpz:cname", cfg, params=params)
|
||||
return {"records": resp}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,74 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
name = inputs.get("name")
|
||||
if not name:
|
||||
raise Exception("name is required")
|
||||
max_results = inputs.get("max_results")
|
||||
|
||||
params = {
|
||||
"name~": name,
|
||||
"_max_results": int(max_results or 100),
|
||||
}
|
||||
resp = request("GET", "/record:host", cfg, params=params)
|
||||
return {"records": resp}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,65 @@
|
||||
import json, os, sys, base64, ssl, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _ctx(cfg):
|
||||
if cfg.get("insecure"):
|
||||
c = ssl.create_default_context()
|
||||
c.check_hostname = False
|
||||
c.verify_mode = ssl.CERT_NONE
|
||||
return c
|
||||
return None
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return str(cfg.get("base_url", "")).rstrip("/") + "/wapi/" + str(cfg.get("wapi_version") or "v2.12")
|
||||
|
||||
|
||||
def _auth(cfg):
|
||||
raw = str(cfg.get("username", "")) + ":" + str(cfg.get("password", ""))
|
||||
return "Basic " + base64.b64encode(raw.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = _base(cfg) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Authorization": _auth(cfg), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60, context=_ctx(cfg)) as r:
|
||||
raw = r.read()
|
||||
try:
|
||||
return json.loads(raw) if raw else {}
|
||||
except Exception:
|
||||
return {"result": raw.decode("utf-8", "replace")}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
request("GET", "/grid", cfg)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,75 @@
|
||||
id: netskope
|
||||
name: Netskope
|
||||
version: 1.0.0
|
||||
description: "Netskope (SASE/CASB REST API v2) — web/cloud containment: list and read URL lists, replace a URL list (block list) and deploy it, and list enrolled users/clients. API-token authentication; stdlib-only, no extra Python dependencies."
|
||||
changelog: "1.0.0 — Initial release: list/get URL lists, replace URL list, deploy URL lists, list users."
|
||||
category: network
|
||||
|
||||
# Per-instance configuration. The token is sent as the 'Netskope-Api-Token' header.
|
||||
config_schema:
|
||||
properties:
|
||||
base_url:
|
||||
type: string
|
||||
description: "Netskope tenant URL (e.g. https://yourtenant.goskope.com)"
|
||||
api_token:
|
||||
type: string
|
||||
description: "Netskope API v2 token"
|
||||
x-soar-sensitive: true
|
||||
required:
|
||||
- base_url
|
||||
- api_token
|
||||
|
||||
commands:
|
||||
- id: list_url_lists
|
||||
name: netskope-list-url-lists
|
||||
description: "List URL lists."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: get_url_list
|
||||
name: netskope-get-url-list
|
||||
description: "Get a single URL list by ID."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
list_id: { type: string, description: "URL list ID" }
|
||||
required: [list_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: replace_url_list
|
||||
name: netskope-replace-url-list
|
||||
description: "Replace the URLs of a URL list (e.g. update a block list)."
|
||||
inputs_schema:
|
||||
properties:
|
||||
list_id: { type: string, description: "URL list ID" }
|
||||
name: { type: string, description: "URL list name" }
|
||||
urls: { type: string, description: "Comma-separated URLs/domains that become the list" }
|
||||
list_type: { type: string, description: "exact or regex (default exact)" }
|
||||
required: [list_id, name, urls]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: deploy_url_lists
|
||||
name: netskope-deploy-url-lists
|
||||
description: "Deploy pending URL-list changes so they take effect."
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: list_users
|
||||
name: netskope-list-users
|
||||
description: "List enrolled users (SCIM)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
limit: { type: number, description: "Max users (default 50)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
|
||||
- id: test_connection
|
||||
name: netskope-test-connection
|
||||
description: "Verify connectivity and the API token (used by the Test button)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
@@ -0,0 +1,44 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = str(cfg.get("base_url", "")).rstrip("/") + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Netskope-Api-Token": str(cfg.get("api_token", "")), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
result = request("POST", "/api/v2/policy/urllist/deploy", cfg, body={})
|
||||
return result if result else {"ok": True}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,47 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = str(cfg.get("base_url", "")).rstrip("/") + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Netskope-Api-Token": str(cfg.get("api_token", "")), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
list_id = inputs.get("list_id")
|
||||
if not list_id:
|
||||
raise Exception("list_id is required")
|
||||
q = lambda v: urllib.parse.quote(str(v), safe="")
|
||||
return request("GET", "/api/v2/policy/urllist/" + q(list_id), cfg)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,43 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = str(cfg.get("base_url", "")).rstrip("/") + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Netskope-Api-Token": str(cfg.get("api_token", "")), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
return request("GET", "/api/v2/policy/urllist", cfg)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,44 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = str(cfg.get("base_url", "")).rstrip("/") + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Netskope-Api-Token": str(cfg.get("api_token", "")), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
limit = inputs.get("limit")
|
||||
return request("GET", "/api/v2/scim/Users", cfg, params={"count": int(limit or 50)})
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,58 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = str(cfg.get("base_url", "")).rstrip("/") + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Netskope-Api-Token": str(cfg.get("api_token", "")), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
list_id = inputs.get("list_id")
|
||||
if not list_id:
|
||||
raise Exception("list_id is required")
|
||||
name = inputs.get("name")
|
||||
if not name:
|
||||
raise Exception("name is required")
|
||||
urls_raw = inputs.get("urls")
|
||||
if not urls_raw:
|
||||
raise Exception("urls is required")
|
||||
urls_list = [s.strip() for s in str(urls_raw).split(",") if s.strip()]
|
||||
if not urls_list:
|
||||
raise Exception("urls is required")
|
||||
list_type = inputs.get("list_type") or "exact"
|
||||
q = lambda v: urllib.parse.quote(str(v), safe="")
|
||||
body = {"name": name, "data": {"type": list_type, "urls": urls_list}}
|
||||
return request("PUT", "/api/v2/policy/urllist/" + q(list_id), cfg, body=body)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,44 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def request(method, path, cfg, body=None, params=None):
|
||||
url = str(cfg.get("base_url", "")).rstrip("/") + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
data = json.dumps(body).encode("utf-8") if body is not None else None
|
||||
headers = {"Netskope-Api-Token": str(cfg.get("api_token", "")), "Accept": "application/json"}
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
print(json.dumps(fn(_cfg(), _inputs())))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, inputs):
|
||||
request("GET", "/api/v2/policy/urllist", cfg)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,93 @@
|
||||
id: zscaler_zpa
|
||||
name: Zscaler Private Access
|
||||
version: 1.0.0
|
||||
description: "Zscaler Private Access (ZPA config API) — zero-trust inventory and policy visibility: list and read application segments, list servers, server groups and segment groups, and list access policy rules. OAuth2 client-credentials authentication; stdlib-only, no extra Python dependencies."
|
||||
changelog: "1.0.0 — Initial release: list/get application segments, list servers, server groups, segment groups, access policy rules."
|
||||
category: network
|
||||
|
||||
# Per-instance configuration. Sign in with client_id + client_secret to obtain a
|
||||
# bearer token; customer_id scopes the config API.
|
||||
config_schema:
|
||||
properties:
|
||||
base_url:
|
||||
type: string
|
||||
description: "ZPA config API base URL"
|
||||
default: "https://config.private.zscaler.com"
|
||||
client_id:
|
||||
type: string
|
||||
description: "ZPA API client ID"
|
||||
client_secret:
|
||||
type: string
|
||||
description: "ZPA API client secret"
|
||||
x-soar-sensitive: true
|
||||
customer_id:
|
||||
type: string
|
||||
description: "ZPA customer ID"
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
- customer_id
|
||||
|
||||
commands:
|
||||
- id: list_application_segments
|
||||
name: zpa-list-application-segments
|
||||
description: "List application segments."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
page: { type: number, description: "Page number (default 1)" }
|
||||
page_size: { type: number, description: "Page size (default 20)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: get_application_segment
|
||||
name: zpa-get-application-segment
|
||||
description: "Get a single application segment by ID."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
segment_id: { type: string, description: "Application segment ID" }
|
||||
required: [segment_id]
|
||||
outputs_schema: { properties: {} }
|
||||
- id: list_servers
|
||||
name: zpa-list-servers
|
||||
description: "List application servers."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties:
|
||||
page: { type: number, description: "Page number (default 1)" }
|
||||
page_size: { type: number, description: "Page size (default 20)" }
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: list_server_groups
|
||||
name: zpa-list-server-groups
|
||||
description: "List server groups."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: list_segment_groups
|
||||
name: zpa-list-segment-groups
|
||||
description: "List segment groups."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
- id: list_access_policies
|
||||
name: zpa-list-access-policies
|
||||
description: "List access policy rules."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
|
||||
- id: test_connection
|
||||
name: zpa-test-connection
|
||||
description: "Verify the sign-in and API access (used by the Test button)."
|
||||
risk: read
|
||||
inputs_schema:
|
||||
properties: {}
|
||||
required: []
|
||||
outputs_schema: { properties: {} }
|
||||
@@ -0,0 +1,69 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
q = lambda v: urllib.parse.quote(str(v), safe="")
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
segment_id = inputs.get("segment_id")
|
||||
if not segment_id:
|
||||
raise Exception("segment_id is required")
|
||||
return request("GET", "/application/" + q(segment_id), cfg, token)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,63 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
return request("GET", "/policySet/rules/policyType/ACCESS_POLICY", cfg, token)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,65 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
page = inputs.get("page") or 1
|
||||
page_size = inputs.get("page_size") or 20
|
||||
return request("GET", "/application", cfg, token, params={"page": int(page), "pagesize": int(page_size)})
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,63 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
return request("GET", "/segmentGroup", cfg, token)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,63 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
return request("GET", "/serverGroup", cfg, token)
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,65 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
page = inputs.get("page") or 1
|
||||
page_size = inputs.get("page_size") or 20
|
||||
return request("GET", "/server", cfg, token, params={"page": int(page), "pagesize": int(page_size)})
|
||||
|
||||
|
||||
_run(main)
|
||||
@@ -0,0 +1,64 @@
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
|
||||
def _cfg():
|
||||
return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))
|
||||
|
||||
|
||||
def _inputs():
|
||||
return json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
|
||||
|
||||
|
||||
def _base(cfg):
|
||||
return (str(cfg.get("base_url") or "https://config.private.zscaler.com")).rstrip("/")
|
||||
|
||||
|
||||
def _token(cfg):
|
||||
form = urllib.parse.urlencode({
|
||||
"client_id": str(cfg.get("client_id", "")),
|
||||
"client_secret": str(cfg.get("client_secret", "")),
|
||||
}).encode("utf-8")
|
||||
req = urllib.request.Request(_base(cfg) + "/signin", data=form,
|
||||
headers={"Content-Type": "application/x-www-form-urlencoded",
|
||||
"Accept": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=60) as r:
|
||||
tok = json.loads(r.read())
|
||||
if not tok.get("access_token"):
|
||||
raise Exception("Sign-in failed: " + json.dumps(tok))
|
||||
return tok["access_token"]
|
||||
|
||||
|
||||
def request(method, path, cfg, token, params=None):
|
||||
# path is relative to the customer config base, e.g. /application
|
||||
url = _base(cfg) + "/mgmtconfig/v1/admin/customers/" + str(cfg.get("customer_id", "")) + path
|
||||
if params:
|
||||
clean = {k: v for k, v in params.items() if v not in (None, "")}
|
||||
if clean:
|
||||
url += "?" + urllib.parse.urlencode(clean)
|
||||
headers = {"Authorization": "Bearer " + token, "Accept": "application/json"}
|
||||
req = urllib.request.Request(url, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=90) as r:
|
||||
raw = r.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
|
||||
|
||||
def _run(fn):
|
||||
try:
|
||||
cfg = _cfg()
|
||||
inputs = _inputs()
|
||||
token = _token(cfg)
|
||||
print(json.dumps(fn(cfg, token, inputs)))
|
||||
except urllib.error.HTTPError as e:
|
||||
print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e)}))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main(cfg, token, inputs):
|
||||
request("GET", "/application", cfg, token, params={"page": 1, "pagesize": 1})
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
_run(main)
|
||||
Reference in New Issue
Block a user