Skip to content

Commit ecb6e76

Browse files
committed
fix Cb Defense query parameters & implement Mutable model for Cb Defense objects
1 parent e7a2612 commit ecb6e76

File tree

5 files changed

+302
-11
lines changed

5 files changed

+302
-11
lines changed

src/cbapi/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ def api_json_request(self, method, uri, **kwargs):
223223
headers = kwargs.pop("headers", {})
224224
raw_data = None
225225

226-
if method in ("POST", "PUT"):
226+
if method in ("POST", "PUT", "PATCH"):
227227
if "Content-Type" not in headers:
228228
headers["Content-Type"] = "application/json"
229229
raw_data = kwargs.pop("data", {})

src/cbapi/defense/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
from __future__ import absolute_import
44

5-
from .rest_api import CbDefenseAPI
5+
from .rest_api import CbDefenseAPI
6+
from .models import Device, Event

src/cbapi/defense/models.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,69 @@
11
from ..models import MutableBaseModel, CreatableModelMixin, NewBaseModel
22

3+
import logging
4+
import json
5+
from ..errors import ServerError
36

4-
class Device(NewBaseModel):
7+
log = logging.getLogger(__name__)
8+
9+
10+
class DefenseMutableModel(MutableBaseModel):
11+
_change_object_http_method = "PATCH"
12+
13+
def _update_object(self):
14+
if self.__class__.primary_key in self._dirty_attributes.keys() or self._model_unique_id is None:
15+
log.debug("Creating a new {0:s} object".format(self.__class__.__name__))
16+
ret = self._cb.api_json_request(self.__class__._new_object_http_method, self.urlobject,
17+
data=self._info)
18+
else:
19+
log.debug("Updating {0:s} with unique ID {1:s}".format(self.__class__.__name__, str(self._model_unique_id)))
20+
ret = self._cb.api_json_request(self.__class__._change_object_http_method,
21+
self._build_api_request_uri(), data=self._dirty_attributes)
22+
23+
return self._refresh_if_needed(ret)
24+
25+
def _refresh_if_needed(self, request_ret):
26+
refresh_required = True
27+
28+
if request_ret.status_code not in range(200, 300):
29+
try:
30+
message = json.loads(request_ret.content)[0]
31+
except:
32+
message = request_ret.content
33+
34+
raise ServerError(request_ret.status_code, message,
35+
result="Did not update {} record.".format(self.__class__.__name__))
36+
else:
37+
try:
38+
message = request_ret.json()
39+
log.debug("Received response: %s" % message)
40+
if not isinstance(message, dict):
41+
raise ServerError(request_ret.status_code, message,
42+
result="Unknown error updating {0:s} record.".format(self.__class__.__name__))
43+
else:
44+
if message.get("success", False):
45+
if isinstance(message.get(self.info_key, None), dict):
46+
self._info = message.get(self.info_key)
47+
self._full_init = True
48+
refresh_required = False
49+
else:
50+
# "success" is False
51+
raise ServerError(request_ret.status_code, message.get("message", ""),
52+
result="Did not update {0:s} record.".format(self.__class__.__name__))
53+
except:
54+
pass
55+
56+
self._dirty_attributes = {}
57+
if refresh_required:
58+
self.refresh()
59+
return self._model_unique_id
60+
61+
62+
class Device(DefenseMutableModel):
563
urlobject = "/integrationServices/v3/device"
664
primary_key = "deviceId"
65+
info_key = "deviceInfo"
66+
swagger_meta_file = "defense/models/deviceInfo.yaml"
767

868
def __init__(self, cb, model_unique_id, initial_data=None):
969
super(Device, self).__init__(cb, model_unique_id, initial_data)
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
type: object
2+
properties:
3+
osVersion:
4+
type: string
5+
activationCode:
6+
type: string
7+
organizationId:
8+
type: integer
9+
format: int64
10+
deviceId:
11+
type: integer
12+
format: int64
13+
deviceSessionId:
14+
type: integer
15+
format: int64
16+
deviceOwnerId:
17+
type: integer
18+
format: int64
19+
deviceGuid:
20+
type: string
21+
format: uuid
22+
email:
23+
type: string
24+
format: email
25+
assignedToId:
26+
type: integer
27+
format: int64
28+
assignedToName:
29+
type: string
30+
deviceType:
31+
type: string
32+
x-nullable: true
33+
enum:
34+
- "MAC"
35+
- "WINDOWS"
36+
firstName:
37+
type: string
38+
lastName:
39+
type: string
40+
middleName:
41+
type: string
42+
createTime:
43+
type: integer
44+
format: int64
45+
policyId:
46+
type: integer
47+
format: int64
48+
policyName:
49+
type: string
50+
quarantined:
51+
type: boolean
52+
targetPriorityType:
53+
type: string
54+
x-nullable: true
55+
enum:
56+
- "HIGH"
57+
- "LOW"
58+
- "MEDIUM"
59+
- "MISSION_CRITICAL"
60+
lastVirusActivityTime:
61+
type: integer
62+
format: int64
63+
firstVirusActivityTime:
64+
type: integer
65+
format: int64
66+
activationCodeExpiryTime:
67+
type: integer
68+
format: int64
69+
organizationName:
70+
type: string
71+
sensorVersion:
72+
type: string
73+
registeredTime:
74+
type: integer
75+
format: int64
76+
lastContact:
77+
type: integer
78+
format: int64
79+
lastReportedTime:
80+
type: integer
81+
format: int64
82+
windowsPlatform:
83+
type: string
84+
x-nullable: true
85+
enum:
86+
- "CLIENT_X64"
87+
- "CLIENT_X86"
88+
- "SERVER_X6"
89+
- "SERVER_X86"
90+
vdiBaseDevice:
91+
type: integer
92+
format: int64
93+
avStatus:
94+
type: array
95+
items:
96+
type: string
97+
x-nullable: true
98+
enum:
99+
- "AV_ACTIVE"
100+
- "AV_BYPASS"
101+
- "AV_DEREGISTERED"
102+
- "AV_NOT_REGISTERED"
103+
- "AV_REGISTERED"
104+
- "FULLY_DISABLED"
105+
- "FULLY_ENABLED"
106+
- "INSTALLED"
107+
- "INSTALLED_SERVER"
108+
- "NOT_INSTALLED"
109+
- "ONACCESS_SCAN_DISABLED"
110+
- "ONDEMAND_SCAN_DISABLED"
111+
- "ONDEMOND_SCAN_DISABLED"
112+
- "PRODUCT_UPDATE_DISABLED"
113+
- "SIGNATURE_UPDATE_DISABLED"
114+
- "UNINSTALLED"
115+
- "UNINSTALLED_SERVER"
116+
deregisteredTime:
117+
type: integer
118+
format: int64
119+
sensorStates:
120+
type: array
121+
items:
122+
type: string
123+
x-nullable: true
124+
enum:
125+
- "ACTIVE"
126+
- "CSR_ACTION"
127+
- "DB_CORRUPTION_DETECTED"
128+
- "DRIVER_INIT_ERROR"
129+
- "LOOP_DETECTED"
130+
- "PANICS_DETECTED"
131+
- "REMGR_INIT_ERROR"
132+
- "REPUX_ACTION"
133+
- "SENSOR_MAINTENANCE"
134+
- "SENSOR_RESET_IN_PROGRESS"
135+
- "SENSOR_SHUTDOWN"
136+
- "SENSOR_UNREGISTERED"
137+
- "SENSOR_UPGRADE_IN_PROGRESS"
138+
- "UNSUPPORTED_OS"
139+
- "WATCHDOG"
140+
messages:
141+
type: array
142+
items:
143+
type: object
144+
properties:
145+
message:
146+
type: string
147+
time:
148+
type: integer
149+
format: int64
150+
rootedBySensor:
151+
type: boolean
152+
rootedBySensorTime:
153+
type: integer
154+
format: int64
155+
lastInternalIpAddress:
156+
type: string
157+
lastExternalIpAddress:
158+
type: string
159+
lastLocation:
160+
type: string
161+
x-nullable: true
162+
enum:
163+
- "OFFSITE"
164+
- "ONSITE"
165+
- "UNKNOWN"
166+
avUpdateServers:
167+
type: array
168+
items:
169+
type: string
170+
passiveMode:
171+
type: boolean
172+
lastResetTime:
173+
type: integer
174+
format: int64
175+
lastShutdownTime:
176+
type: integer
177+
format: int64
178+
scanStatus:
179+
type: string
180+
scanLastActionTime:
181+
type: integer
182+
format: int64
183+
scanLastCompleteTime:
184+
type: integer
185+
format: int64
186+
linuxKernelVersion:
187+
type: string
188+
avEngine:
189+
type: string
190+
avLastScanTime:
191+
type: integer
192+
format: int64
193+
rootedByAnalytics:
194+
type: boolean
195+
rootedByAnalyticsTime:
196+
type: integer
197+
format: int64
198+
testId:
199+
type: integer
200+
avMaster:
201+
type: boolean
202+
uninstalledTime:
203+
type: integer
204+
format: int64
205+
name:
206+
type: string
207+
status:
208+
type: string
209+
x-nullable: true
210+
enum:
211+
- "ACTIVE"
212+
- "ALL"
213+
- "BYPASS"
214+
- "BYPASS_ON"
215+
- "DEREGISTERED"
216+
- "ERROR"
217+
- "INACTIVE"
218+
- "PENDING"
219+
- "QUARANTINE"
220+
- "REGISTERED"
221+
- "UNINSTALLED"

src/cbapi/defense/rest_api.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
log = logging.getLogger(__name__)
88

99

10+
def convert_to_kv_pairs(q):
11+
k, v = q.split(':', 1)
12+
return k, v
13+
14+
1015
class CbDefenseAPI(BaseAPI):
1116
"""The main entry point into the Cb Defense API.
1217
@@ -69,7 +74,7 @@ def _clone(self):
6974
def where(self, q):
7075
"""Add a filter to this query.
7176
72-
:param str q: Query string - see the Enterprise Protection API reference <http://developer.carbonblack.com/reference/enterprise-protection/7.2/rest-api/#searching:ec3e4958451e5256ed16afd222059e6d>`_.
77+
:param str q: Query string
7378
:return: Query object
7479
:rtype: :py:class:`Query`
7580
"""
@@ -80,17 +85,23 @@ def where(self, q):
8085
def and_(self, q):
8186
"""Add a filter to this query. Equivalent to calling :py:meth:`where` on this object.
8287
83-
:param str q: Query string - see the Enterprise Protection API reference <http://developer.carbonblack.com/reference/enterprise-protection/7.2/rest-api/#searching:ec3e4958451e5256ed16afd222059e6d>`_.
88+
:param str q: Query string
8489
:return: Query object
8590
:rtype: :py:class:`Query`
8691
"""
8792
return self.where(q)
8893

94+
def prepare_query(self, args):
95+
if self._query:
96+
for qe in self._query:
97+
k, v = convert_to_kv_pairs(qe)
98+
args[k] = v
99+
100+
return args
101+
89102
def _count(self):
90-
# TODO: FIX
91103
args = {'limit': -1}
92-
if self._query:
93-
args['q'] = self._query
104+
args = self.prepare_query(args)
94105

95106
query_args = convert_query_params(args)
96107
self._total_results = int(self._cb.get_object(self._doc_class.urlobject, query_parameters=query_args).get("count", 0))
@@ -107,9 +118,7 @@ def _search(self, start=0, rows=0):
107118
current = start
108119
numrows = 0
109120

110-
if self._query:
111-
args['q'] = self._query
112-
121+
args = self.prepare_query(args)
113122
still_querying = True
114123

115124
while still_querying:

0 commit comments

Comments
 (0)