Skip to content

Commit

Permalink
Prepare for 0.9.3 release:
Browse files Browse the repository at this point in the history
* fix bugs in Sensor searching
* rename Sensor `network_adapters` helper to `network_interfaces`
* add scripts `cbapi-response` and `cbapi-protection`: for now, used to configure the API
  • Loading branch information
jgarman committed Jun 13, 2016
1 parent 7a064cb commit 55962da
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 10 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Python bindings for Carbon Black REST API

**Current version: 0.9.2**
**Current version: 0.9.3**

These are the new Python bindings for the Carbon Black Enterprise Response and Enterprise Protection REST APIs.
To learn more about the REST APIs, visit the Carbon Black Developer Network Website at https://developer.carbonblack.com.
Expand Down
8 changes: 8 additions & 0 deletions bin/cbapi-protection
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python

from cbapi.protection.cli import main
import sys


if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
8 changes: 8 additions & 0 deletions bin/cbapi-response
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python

from cbapi.response.cli import main
import sys


if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
# The short X.Y version.
version = u'0.9'
# The full version, including alpha/beta/rc tags.
release = u'0.9.2'
release = u'0.9.3'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Once you acquire your API token, place it in one of the default credentials file
Credentials found in a later path will overwrite earlier ones.

The credentials are stored in INI format. The name of each credential profile is enclosed in square brackets, followed
by comma separated key-value pairs providing the necessary credential information::
by key-value pairs providing the necessary credential information::

[default]
url=https://localhost
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

setup(
name='cbapi',
version='0.9.2',
version='0.9.3',
url='https://github.com/carbonblack/cbapi-python',
license='MIT',
author='Carbon Black',
Expand All @@ -44,5 +44,6 @@
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules'
]
],
scripts=['bin/cbapi-response', 'bin/cbapi-protection']
)
9 changes: 9 additions & 0 deletions src/cbapi/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ class NewBaseModel(object):
primary_key = "id"

def __init__(self, cb, model_unique_id=None, initial_data=None, force_init=False, full_doc=False):
"""
Base model for
:param cb:
:param model_unique_id:
:param initial_data:
:param force_init:
:param full_doc:
:return:
"""
self._cb = cb
self._last_refresh_time = 0

Expand Down
92 changes: 92 additions & 0 deletions src/cbapi/protection/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env python

import argparse
import contextlib

from six import iteritems
from six.moves import input
import os
import getpass
import six
if six.PY3:
from io import BytesIO as StringIO
else:
from cStringIO import StringIO

from cbapi.response.rest_api import get_api_token
from six.moves.configparser import RawConfigParser


@contextlib.contextmanager
def temp_umask(umask):
oldmask = os.umask(umask)
try:
yield
finally:
os.umask(oldmask)


def configure(opts):
credential_path = os.path.join(os.path.expanduser("~"), ".carbonblack")
credential_file = os.path.join(credential_path, "credentials.protection")

print("Welcome to the CbAPI.")
if os.path.exists(credential_file):
print("An existing credential file exists at {0}.".format(credential_file))
resp = input("Do you want to continue and overwrite the existing configuration? [Y/N] ")
if resp.strip().upper() != "Y":
print("Exiting.")
return 1

if not os.path.exists(credential_path):
os.makedirs(credential_path, 0o700)

url = input("URL to the Cb Protection server [https://hostname]: ")

ssl_verify = None
while ssl_verify not in ["Y", "N"]:
ssl_verify = input("Use SSL/TLS certificate validation (answer 'N' if using self-signed certs) [Y/N]: ")
ssl_verify = ssl_verify.strip().upper()

if ssl_verify == "Y":
ssl_verify = True
else:
ssl_verify = False

token = input("API token: ")

config = RawConfigParser()
config.readfp(StringIO('[default]'))
config.set("default", "url", url)
config.set("default", "token", token)
config.set("default", "ssl_verify", ssl_verify)
with temp_umask(0):
with os.fdopen(os.open(credential_file, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o600), 'w') as fp:
os.chmod(credential_file, 0o600)
config.write(fp)
print("Successfully wrote credentials to {0}.".format(credential_file))


command_map = {
"configure": {
"extra_args": {},
"help": "Configure CbAPI",
"method": configure
}
}


def main(args):
parser = argparse.ArgumentParser()
commands = parser.add_subparsers(dest="command_name", help="CbAPI subcommand")

for cmd_name, cmd_config in iteritems(command_map):
cmd_parser = commands.add_parser(cmd_name, help=cmd_config.get("help", None))
for cmd_arg_name, cmd_arg_config in iteritems(cmd_config.get("extra_args", {})):
cmd_parser.add_argument(cmd_arg_name, **cmd_arg_config)

opts = parser.parse_args(args)
command = command_map.get(opts.command_name)
command_method = command.get("method", None)
if command_method:
return command_method(opts)
12 changes: 10 additions & 2 deletions src/cbapi/protection/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ def fileInstances(self):
def template(self):
return self._join(Computer, "templateComputerId")

def resetCLIPassword(self):
self._build_api_request_uri()
self.refresh()
return getattr(self, "cliPassword")


class Connector(MutableBaseModel, CreatableModelMixin):
urlobject = "/api/bit9platform/v1/connector"
Expand All @@ -111,13 +116,16 @@ def pendingAnalyses(self):
return self._cb.select(PendingAnalysis).where("connectorId:{0:d}".format(self.id))


@immutable
class Event(BaseModel):
class Event(NewBaseModel):
urlobject = "/api/bit9platform/v1/event"

def __init__(self, cb, model_unique_id, initial_data=None):
super(Event, self).__init__(cb, model_unique_id, initial_data)

@property
def fileCatalog(self):
return self._join(FileCatalog, "fileCatalogId")


class FileAnalysis(MutableModel):
urlobject = "/api/bit9platform/v1/fileAnalysis"
Expand Down
106 changes: 106 additions & 0 deletions src/cbapi/response/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env python

import argparse
import contextlib

from six import iteritems
from six.moves import input
import os
import getpass
import six
if six.PY3:
from io import BytesIO as StringIO
else:
from cStringIO import StringIO

from cbapi.response.rest_api import get_api_token
from six.moves.configparser import RawConfigParser


@contextlib.contextmanager
def temp_umask(umask):
oldmask = os.umask(umask)
try:
yield
finally:
os.umask(oldmask)


def configure(opts):
credential_path = os.path.join(os.path.expanduser("~"), ".carbonblack")
credential_file = os.path.join(credential_path, "credentials.response")

print("Welcome to the CbAPI.")
if os.path.exists(credential_file):
print("An existing credential file exists at {0}.".format(credential_file))
resp = input("Do you want to continue and overwrite the existing configuration? [Y/N] ")
if resp.strip().upper() != "Y":
print("Exiting.")
return 1

if not os.path.exists(credential_path):
os.makedirs(credential_path, 0o700)

url = input("URL to the Cb Response server [https://hostname]: ")

ssl_verify = None
while ssl_verify not in ["Y", "N"]:
ssl_verify = input("Use SSL/TLS certificate validation (answer 'N' if using self-signed certs) [Y/N]: ")
ssl_verify = ssl_verify.strip().upper()

if ssl_verify == "Y":
ssl_verify = True
else:
ssl_verify = False

token = input("API token (if unknown, leave blank): ")
if not token:
print("No API token provided: we will look it up using your username & password")
username = input("Username: ")
password = getpass.getpass("Password: ")

print("Retrieving API token for {0}...".format(username))

try:
token = get_api_token(url, username, password, verify=ssl_verify)
except Exception as e:
import traceback
traceback.print_exc()
print("Could not retrieve API token: {0}".format(str(e)))
return 1

config = RawConfigParser()
config.readfp(StringIO('[default]'))
config.set("default", "url", url)
config.set("default", "token", token)
config.set("default", "ssl_verify", ssl_verify)
with temp_umask(0):
with os.fdopen(os.open(credential_file, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o600), 'w') as fp:
os.chmod(credential_file, 0o600)
config.write(fp)
print("Successfully wrote credentials to {0}.".format(credential_file))


command_map = {
"configure": {
"extra_args": {},
"help": "Configure CbAPI",
"method": configure
}
}


def main(args):
parser = argparse.ArgumentParser()
commands = parser.add_subparsers(dest="command_name", help="CbAPI subcommand")

for cmd_name, cmd_config in iteritems(command_map):
cmd_parser = commands.add_parser(cmd_name, help=cmd_config.get("help", None))
for cmd_arg_name, cmd_arg_config in iteritems(cmd_config.get("extra_args", {})):
cmd_parser.add_argument(cmd_arg_name, **cmd_arg_config)

opts = parser.parse_args(args)
command = command_map.get(opts.command_name)
command_method = command.get("method", None)
if command_method:
return command_method(opts)
5 changes: 4 additions & 1 deletion src/cbapi/response/live_response_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,10 @@ def request_session(self, sensor_id):

def close_session(self, sensor_id):
with self._session_lock:
self._sessions[sensor_id]._refcount -= 1
try:
self._sessions[sensor_id]._refcount -= 1
except KeyError:
pass

def _send_keepalive(self, session_id):
log.debug("Sending keepalive message for session id {0}".format(session_id))
Expand Down
4 changes: 2 additions & 2 deletions src/cbapi/response/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def hostname(self):
return getattr(self, 'computer_name', None)

@property
def network_adapters(self):
def network_interfaces(self):
out = []
for adapter in getattr(self, 'network_adapters', '').split('|'):
parts = adapter.split(',')
Expand Down Expand Up @@ -313,7 +313,7 @@ def get_installer(self, osname="windows/exe"):


class SensorQuery(SimpleQuery):
valid_field_names = ['ipaddr', 'hostname', 'groupid']
valid_field_names = ['ip', 'hostname', 'groupid']

def __init__(self, cls, cb):
super(SensorQuery, self).__init__(cls, cb)
Expand Down

0 comments on commit 55962da

Please sign in to comment.