Skip to content

Commit

Permalink
Merge pull request #195 from carbonblack/develop
Browse files Browse the repository at this point in the history
1.3.1 merge to master
  • Loading branch information
abowersox-cb authored Jun 15, 2021
2 parents 5398655 + fd7745d commit e5efed5
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 52 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# VMware Carbon Black Cloud Python SDK

**Latest Version:** 1.3.0
**Latest Version:** 1.3.1
<br>
**Release Date:** 08 June 2021
**Release Date:** 15 June 2021

[![Coverage Status](https://coveralls.io/repos/github/carbonblack/carbon-black-cloud-sdk-python/badge.svg?t=Id6Baf)](https://coveralls.io/github/carbonblack/carbon-black-cloud-sdk-python)
[![Codeship Status for carbonblack/carbon-black-cloud-sdk-python](https://app.codeship.com/projects/9e55a370-a772-0138-aae4-129773225755/status?branch=develop)](https://app.codeship.com/projects/402767)
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.3.0
1.3.1
11 changes: 11 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
Changelog
================================
CBC SDK 1.3.1 - Released June 15, 2021
--------------------------------

New Features:

* Allow the SDK to accept a pre-configured ``Session`` object to be used for access, to get around unusual configuration requirements.

Bug Fixes:

* Fix functions in ``Grant`` object for adding a new access profile to a user access grant.

CBC SDK 1.3.0 - Released June 8, 2021
--------------------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
author = 'Developer Relations'

# The full version, including alpha/beta/rc tags
release = '1.3.0'
release = '1.3.1'


# -- General configuration ---------------------------------------------------
Expand Down
3 changes: 2 additions & 1 deletion docs/guides-and-resources.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Guides
* :doc:`workload` - Advanced protection purpose-built for securing modern workloads to reduce the attack surface and strengthen security posture.
* :doc:`reputation-override` - Manage reputation overrides for known applications, IT tools or certs.
* :doc:`live-response` - Live Response allows security operators to collect information and take action on remote endpoints in real time.
* :doc:`unified-binary-store` - The unified binary store (UBS) is responsible for storing all binaries and corresponding metadata for those binaries.
* :doc:`unified-binary-store` - The unified binary store (UBS) is responsible for storing all binaries and corresponding metadata for those binaries.
* :doc:`users-grants` - Work with users and access grants.

Examples
--------
Expand Down
198 changes: 198 additions & 0 deletions docs/users-grants.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
Users and Grants
================

Using the Carbon Black Cloud SDK, you can work with the users in your organization, as well as their access grants
and profiles.

Uniform Resource Names (URNs)
-----------------------------

The various API functions that work with users and grants often make use of *uniform resource names* (URNs) that
uniquely represent various pieces of the Carbon Black Cloud environment. These pieces include:

* **Organizations,** represented as ``psc:org:ORGKEY``, where ``ORGKEY`` is the organization's alphanumeric key value.
* The special URN ``psc:org:ORKGEY:CHILDREN``, where ``ORGKEY`` is the organization's alphanumeric key value,
refers to all the *child organizations* of that organization, but *not* the organization itself.
* **Users,** represented as ``psc:user:ORGKEY:USERID``, where ``ORGKEY`` is the organization's alphanumeric key value
and ``USERID`` is the user's numeric login ID.
* **Access roles,** represented as ``psc:role:OPT-ORGKEY:NAME``, where ``OPT-ORGKEY`` is (optionally) the alphanumeric
key value of the organization containing that role, and ``NAME`` is the name of the role. A role that does not have
an OPT-ORGKEY is a default/global role created for all organizations.

Most of these are dealt with for you by the Carbon Black Cloud SDK.

Getting a List of Users
-----------------------

We can do a query on the ``User`` object to get a list of users within the organization we're accessing.

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> query = api.select(User)
>>> user_list = list(query)
>>> for user in user_list:
... print(f"{user.first_name} {user.last_name} (#{user.login_id}) <{user.email}>")
...
Lysa Arryn (#2345670) <[email protected]>
Olenna Redwyne (#2345671) <[email protected]>
Arianne Martell (#2345672) <[email protected]>
Jorah Mormont (#2345673) <[email protected]>

We can restrict the query by user IDs or E-mail addresses by using the ``user_ids([str])`` or ``email_addresses([str])``
methods on the query object returned by ``select()`` before enumerating its results.

Modifying a User
----------------

A ``User`` can be modified by changing one or more of its fields and then calling its ``save()`` method.

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> user = api.select(User, 2345672)
>>> print(user.phone)
800-555-0000
>>> user.phone = '888-555-9753'
>>> user.save()
<cbc_sdk.platform.users.User: id 2345672> @ https://defense.conferdeploy.net (*)
>>> print(user.phone)
888-555-9753

**Note:** A user's *role* can only be modified by updating the user's *access grant,* detailed below.

Creating a New User
-------------------

Creating a user may be done with the help of a *builder object,* which is returned from the ``User.create()``
function.

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> builder = User.create(api)
>>> builder.set_first_name('Samwell').set_last_name('Tarly')
<cbc_sdk.platform.users.User.UserBuilder object at 0x00000209B8123D00>
>>> builder.set_email('[email protected]').set_phone('800-555-8008')
<cbc_sdk.platform.users.User.UserBuilder object at 0x00000209B8123D00>
>>> builder.set_role('psc:role::BETA_SYSTEM_ADMIN')
<cbc_sdk.platform.users.User.UserBuilder object at 0x00000209B8123D00>
>>> builder.build()

Alternately, you may construct a *template object* (a Python ``dict``) that contains the user's information and
create the user directly.

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> user_template = {'first_name': 'Selyse', 'last_name': 'Florent', 'email': '[email protected]',
... 'phone': '877-555-9099', 'role_urn': 'psc:role::BETA_SYSTEM_ADMIN'}
>>> User.create(api, user_template)

**Note:** A user that has just been created will *not* be visible in either the UI or in a ``User`` query as detailed
above, until the user activates their account through the invitation E-mail message and sets a password.

User Access Grants
------------------

Every user object has an *access grant* object associated with it, defining the access roles they are permitted to use.
You can use the ``grant()`` method on a ``User`` to get the grant and inspect or modify it.

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> user = api.select(User, 2345672)
>>> print(f"{user.first_name} {user.last_name}")
Arianne Martell
>>> grant = user.grant()
>>> print(grant.roles)
['psc:role::BETA_SYSTEM_ADMIN']
>>> grant.roles = ['psc:role::BETA_VIEW_ONLY']
>>> grant.save()
<cbc_sdk.platform.grants.Grant: id psc:user:1A2B3C4DE:2345672> @ https://defense.conferdeploy.net
>>> print(grant.roles)
['psc:role::psc:role::BETA_VIEW_ONLY']

You can see what roles your API key is able to access and assign using the ``get_permitted_role_urns()`` function:

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import Grant
>>> for index, role_urn in enumerate(Grant.get_permitted_role_urns(api)):
... print(f"{index}. {role_urn}")
...
0. psc:role::BETA_LEVEL_3_ANALYST
1. psc:role::KUBERNETES_SECURITY_DATAPLANE_ONLY
2. psc:role::ALL_AND_LR
3. psc:role::BETA_LEVEL_1_ANALYST
4. psc:role::BETA_SYSTEM_ADMIN
5. psc:role::KUBERNETES_SECURITY_DATAPLANE
6. psc:role::VIEW_ONLY
7. psc:role::ALL
8. psc:role::KUBERNETES_SECURITY_ADMIN_USER
9. psc:role::BETA_SUPER_ADMIN
10. psc:role::KUBERNETES_SECURITY_READ_ONLY_USER
11. psc:role::CONTAINER_IMAGE_CLI_TOOL
12. psc:role::KUBERNETES_SECURITY_DEVOPS
13. psc:role::BETA_VIEW_ALL
14. psc:role::KUBERNETES_SECURITY_DEVOPS_VIEW_ONLY
15. psc:role::BETA_LEVEL_2_ANALYST
16. psc:role::KUBERNETES_SECURITY_DEVELOPER

Users created in the Carbon Black Cloud console employ *access profiles* on the access grants, which allow roles for
a user to be specified for the organization and/or any child organizations. Access profiles may be accessed and
manipulated through the access grant object.

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> user = api.select(User, 3456789)
>>> grant = user.grant()
>>> for profile in grant.profiles_:
... print(f"{profile.allowed_orgs} - {profile.roles}")
...
['psc:org:1A2B3C4DE'] - ['psc:role::BETA_LEVEL_3_ANALYST']
['psc:org:2F3G4H5JK'] - ['psc:role::BETA_LEVEL_1_ANALYST']

Adding an access profile may be done via the ``create_profile()`` method on ``Grant``:

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> user = api.select(User, 3450987)
>>> grant = user.grant()
>>> builder = grant.create_profile()
>>> builder.add_org('psc:org:2F3G4H5JK').add_role('psc:role::BETA_VIEW_ALL')
<cbc_sdk.platform.grants.Grant.ProfileBuilder object at 0x00000232942C8400>
>>> profile = builder.build()
{'orgs': {'allow': ['psc:org:2F3G4H5JK']}, 'roles': ['psc:role::BETA_VIEW_ALL']}

Or it may be added via a template object (as with ``User``):

::

>>> from cbc_sdk import CBCloudAPI
>>> api = CBCloudAPI(profile='sample')
>>> from cbc_sdk.platform import User
>>> user = api.select(User, 3450987)
>>> grant = user.grant()
>>> profile_template = {'orgs': {'allow': ['psc:org:2F3G4H5JK']}, 'roles': ['psc:role::BETA_VIEW_ALL']}
>>> profile = grant.create_profile(profile_template)
{'orgs': {'allow': ['psc:org:2F3G4H5JK']}, 'roles': ['psc:role::BETA_VIEW_ALL']}

2 changes: 1 addition & 1 deletion src/cbc_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
__author__ = 'Carbon Black Developer Network'
__license__ = 'MIT'
__copyright__ = 'Copyright 2020-2021 VMware Carbon Black'
__version__ = '1.3.0'
__version__ = '1.3.1'

from .rest_api import CBCloudAPI
from .cache import lru
33 changes: 27 additions & 6 deletions src/cbc_sdk/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,13 @@ def init_poolmanager(self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool
class Connection(object):
"""Object that encapsulates the HTTP connection to the CB server."""

def __init__(self, credentials, integration_name=None, timeout=None, max_retries=None, **pool_kwargs):
def __init__(self,
credentials,
integration_name=None,
timeout=None,
max_retries=None,
proxy_session=None,
**pool_kwargs):
"""
Initialize the Connection object.
Expand All @@ -163,6 +169,7 @@ def __init__(self, credentials, integration_name=None, timeout=None, max_retries
integration_name (str): The integration name being used.
timeout (int): The timeout value to use for HTTP requests on this connection.
max_retries (int): The maximum number of times to retry a request.
proxy_session (requests.Session) custom session to be used
**pool_kwargs: Additional arguments to be used to initialize connection pooling.
Raises:
Expand Down Expand Up @@ -195,7 +202,12 @@ def __init__(self, credentials, integration_name=None, timeout=None, max_retries

self.token = credentials.token
self.token_header = {'X-Auth-Token': self.token, 'User-Agent': user_agent}
self.session = requests.Session()
if proxy_session:
self.session = proxy_session
credentials.use_custom_proxy_session = True
else:
self.session = requests.Session()
credentials.use_custom_proxy_session = False

self._timeout = timeout

Expand All @@ -215,7 +227,10 @@ def __init__(self, credentials, integration_name=None, timeout=None, max_retries
self.session.mount(self.server, tls_adapter)

self.proxies = {}
if credentials.ignore_system_proxy: # see https://github.com/kennethreitz/requests/issues/879
if credentials.use_custom_proxy_session:
# get the custom session proxies
self.proxies = self.session.proxies
elif credentials.ignore_system_proxy: # see https://github.com/kennethreitz/requests/issues/879
# Unfortunately, requests will look for any proxy-related environment variables and use those anyway. The
# only way to solve this without side effects, is passing in empty strings for 'http' and 'https':
self.proxies = {
Expand Down Expand Up @@ -380,13 +395,19 @@ def __init__(self, *args, **kwargs):

# must be None to use MAX_RETRIES in Connection __init__
max_retries = kwargs.pop("max_retries", None)
proxy_session = kwargs.pop("proxy_session", None)
pool_connections = kwargs.pop("pool_connections", 1)
pool_maxsize = kwargs.pop("pool_maxsize", DEFAULT_POOLSIZE)
pool_block = kwargs.pop("pool_block", DEFAULT_POOLBLOCK)

self.session = Connection(self.credentials, integration_name=integration_name, timeout=timeout,
max_retries=max_retries, pool_connections=pool_connections,
pool_maxsize=pool_maxsize, pool_block=pool_block)
self.session = Connection(self.credentials,
integration_name=integration_name,
timeout=timeout,
max_retries=max_retries,
proxy_session=proxy_session,
pool_connections=pool_connections,
pool_maxsize=pool_maxsize,
pool_block=pool_block)

def raise_unless_json(self, ret, expected):
"""
Expand Down
Loading

0 comments on commit e5efed5

Please sign in to comment.