""" Python bindings for Bit9Platform API Copyright Bit9, Inc. 2015 support@bit9.com Disclaimer +++++++++++++++++++ By accessing and/or using the samples scripts provided on this site (the "Scripts"), you hereby agree to the following terms: The Scripts are examples provided for purposes of illustration only and are not intended to represent specific recommendations or solutions for API integration activities as use cases can vary widely. THE SCRIPTS ARE PROVIDED "AS IS" WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. BIT9 MAKES NO REPRESENTATION OR OTHER AFFIRMATION OF FACT, INCLUDING BUT NOT LIMITED TO STATEMENTS REGARDING THE SCRIPTS' SUITABILITY FOR USE OR PERFORMANCE. IN NO EVENT SHALL BIT9 BE LIABLE FOR SPECIAL, INCIDENTAL, CONSEQUENTIAL, EXEMPLARY OR OTHER INDIRECT DAMAGES OR FOR DIRECT DAMAGES ARISING OUT OF OR RESULTING FROM YOUR ACCESS OR USE OF THE SCRIPTS, EVEN IF BIT9 IS ADVISED OF OR AWARE OF THE POSSIBILITY OF SUCH DAMAGES. """ import json import requests import logging log = logging.getLogger(__name__) class bit9Api(object): def __init__(self, server, ssl_verify=True, token=None): """ Requires: server - URL to the Bit9Platform server. Usually the same as the web GUI. sslVerify - verify server SSL certificate token - this is token for API interface provided by Bit9 administrator """ if not server.startswith("https"): raise TypeError("Server must be URL: e.g, https://bit9server.local") if token is None: raise TypeError("Missing required authentication token.") self.server = server.rstrip("/") if '/api/bit9platform' not in self.server: self.server = self.server + '/api/bit9platform' self.sslVerify = ssl_verify self.tokenHeader = {'X-Auth-Token': token} self.tokenHeaderJson = {'X-Auth-Token': token, 'content-type': 'application/json'} # Private function that downloads file in chunks def __download_file(self, obj_id, obj_name, local_path, chunk_size_kb=10): # NOTE the stream=True parameter url = self.server + '/' + obj_name + '?id=' + str(obj_id) + '&downloadFile=true' r = requests.get(url, headers=self.tokenHeaderJson, verify=self.sslVerify, stream=True) r.raise_for_status() n = 0 with open(local_path, 'wb') as f: for chunk in r.iter_content(chunk_size=chunk_size_kb*1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.flush() n += 1 def __check_result(self, r): if 400 <= r.status_code < 500: log.error('%s Client Error: %s, %s' % (r.status_code, r.reason, r.text)) elif 500 <= r.status_code < 600: log.error('%s Server Error: %s, %s' % (r.status_code, r.reason, r.text)) elif r.text != '': return r.json() return False # Download file from server to the local file system from fileUpload object def retrieve_uploaded_file(self, obj_id, local_path): return self.__download_file(obj_id, 'v1/fileUpload', local_path) # Download file from server to the local file system from pendingAnalysis object def retrieve_analyzed_file(self, obj_id, local_path): return self.__download_file(obj_id, 'v1/pendingAnalysis', local_path) # Retrieve object using HTTP GET request. Note that this function supports searching as well. # Optional parameters are obj_id that attempts to retrieve specific object, or url_params that can be used # for searching def retrieve(self, api_obj, obj_id=0, url_params=''): if obj_id: api_obj = api_obj + '/' + str(obj_id) if url_params: url_params = '?' + url_params.lstrip("?") url = self.server + '/' + api_obj + url_params r = requests.get(url, headers=self.tokenHeaderJson, verify=self.sslVerify) return self.__check_result(r) # Search object for specific conditions. Optionally sort and/or group results # Offset and limit determines the output window in result set. # example: # res = bit9.search('v1/computer', ['policyName:development', 'ipAddress!192.168.0.*'], sort='name') def search(self, api_obj, search_conditions=[], sort=None, group_by=None, offset=0, limit=1000): query = '&q='.join(search_conditions) if len(query)>0: query = '&q='+query if sort and len(sort)>0: query = query + '&sort=' + sort if group_by and len(group_by)>0: query = query + '&group=' + group_by query = query + '&offset=' + str(offset) + '&limit=' + str(limit) if len(query)>0: query = '?'+query.lstrip("&") url = self.server + '/' + api_obj + query r = requests.get(url, headers=self.tokenHeaderJson, verify=self.sslVerify) return self.__check_result(r) # Create object using HTTP POST request. Note that this can also be used to update existing object def create(self, api_obj, data, url_params=''): if not data: raise TypeError("Missing object data.") if url_params: url_params = '?' + url_params.lstrip("?") url = self.server + '/' + api_obj + url_params r = requests.post(url, data=json.dumps(data), headers=self.tokenHeaderJson, verify=self.sslVerify) return self.__check_result(r) # Update object using HTTP PUT request def update(self, api_obj, data, obj_id=0, url_params=''): if not data: raise TypeError("Missing object data.") if url_params: url_params = '?' + url_params.lstrip("?") if not obj_id: obj_id = data['id'] url = self.server + '/' + api_obj + '/' + str(obj_id) + url_params r = requests.put(url, data=json.dumps(data), headers=self.tokenHeaderJson, verify=self.sslVerify) return self.__check_result(r) # Delete object using HTTP DELETE request. def delete(self, api_obj, data=None, obj_id=0, url_params=''): if not obj_id and data: obj_id = data['id'] if url_params: url_params = '?' + url_params.lstrip("?") if not obj_id: raise TypeError("Missing object data or id.") url = self.server + '/' + api_obj + '/' + str(obj_id) + url_params r = requests.delete(url, headers=self.tokenHeaderJson, verify=self.sslVerify) return self.__check_result(r)