Skip to content

Commit

Permalink
[DEPLOY] v0.8.3 - MSSP Direct Authentication (#427)
Browse files Browse the repository at this point in the history
* Bump version -> 0.8.3

* Expand unit testing to cover new code paths

* Add MSSP Direct Authentication

* Add MSSP Direct Authentication

* Update CHANGELOG.md
  • Loading branch information
jshcodes authored Nov 11, 2021
1 parent 555d413 commit e3bfd9a
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 18 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Version 0.8.3
## Added features and functionality
+ Added: MSSP Direct Authentication - Additional authentication keyword is now available, `member_cid`, allowing developers targeting MSSP functionality to make use of Direct Authentication as opposed to still using Credential Authentication. This functionality is supported in all Service Classes and the Uber Class.
- `_service_class.py`
- `api_complete.py`
- `oauth2.py`
- `tests/test_authorization.py`

# Version 0.8.2
## Issues resolved
+ Fixed: Issue in `_util.args_to_params` when handling Python reserved words defined as keys incorrectly in the parameter dictionary. Closes #422.
Expand Down
9 changes: 8 additions & 1 deletion src/falconpy/_service_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,12 @@ def __init__(self: object, auth_object: object = None,
Mutually exclusive to client_id / client_secret.
{
"client_id": "CLIENT_ID_HERE",
"client_secret": "CLIENT_SECRET_HERE"
"client_secret": "CLIENT_SECRET_HERE",
"member_cid": "CHILD_CID_MSSP_ONLY"
}
client_id -- Client ID for the CrowdStrike API. Mutually exclusive to creds.
client_secret -- Client Secret for the CrowdStriek API. Mutually exclusive to creds.
member_cid -- CID of the child account to authenticate to (MSSP only)
validate_payload -- Boolean specifying if body payloads should be validated.
Defaults to True.
user_agent -- User-Agent string to use for all requests made to the CrowdStrike API.
Expand All @@ -81,6 +83,7 @@ def __init__(self: object, auth_object: object = None,
self.ssl_verify = kwargs.get("ssl_verify", True)
self.timeout = kwargs.get("timeout", None)
user_agent = kwargs.get("user_agent", None)
member_cid = kwargs.get("member_cid", None)
# Currently defaulting to validation enabled
self.validate_payloads = kwargs.get("validate_payloads", True)
self.refreshable = False
Expand All @@ -93,6 +96,10 @@ def __init__(self: object, auth_object: object = None,
"client_id": client_id,
"client_secret": client_secret
}
if member_cid:
# Passing member_cid will not overwrite the
# existing value in the creds dictionary
creds["member_cid"] = member_cid
if auth_object:
self.auth_object = auth_object
if not self.authenticated():
Expand Down
2 changes: 1 addition & 1 deletion src/falconpy/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
For more information, please refer to <https://unlicense.org>
"""
_VERSION = '0.8.2'
_VERSION = '0.8.3'
_MAINTAINER = 'Joshua Hiller'
_AUTHOR = 'CrowdStrike'
_AUTHOR_EMAIL = '[email protected]'
Expand Down
9 changes: 8 additions & 1 deletion src/falconpy/api_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def __init__(self: object, # pylint: disable=R0913
creds: dict = None,
client_id: str = None,
client_secret: str = None,
member_cid: str = None,
ssl_verify: bool = True,
proxy: dict = None,
timeout: float or tuple = None,
Expand All @@ -74,10 +75,12 @@ def __init__(self: object, # pylint: disable=R0913
Mutually exclusive to client_id / client_secret.
{
"client_id": "CLIENT_ID_HERE",
"client_secret": "CLIENT_SECRET_HERE"
"client_secret": "CLIENT_SECRET_HERE",
"member_cid": "CHILD_CID_MSSP_ONLY"
}
client_id -- Client ID for the CrowdStrike API. Mutually exclusive to creds.
client_secret -- Client Secret for the CrowdStrike API. Mutually exclusive to creds.
member_cid -- Child CID to connect to. (MSSP only) Mutually exclusive to creds.
user_agent -- User-Agent string to use for all requests made to the CrowdStrike API.
String. Defaults to crowdstrike-falconpy/VERSION.
Expand All @@ -88,6 +91,10 @@ def __init__(self: object, # pylint: disable=R0913
"client_id": client_id,
"client_secret": client_secret
}
# Have to pass member_cid the same way you pass client_id / secret
# If you use a creds dictionary, pass the member_cid there instead
if member_cid:
creds["member_cid"] = member_cid
elif not creds:
creds = {}
self.creds = creds
Expand Down
12 changes: 9 additions & 3 deletions src/falconpy/oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class OAuth2:
def __init__(self: object, base_url: str = "https://api.crowdstrike.com",
ssl_verify: bool = True, proxy: dict = None, timeout: float or tuple = None,
creds: dict = None, client_id: str = None, client_secret: str = None,
user_agent: str = None):
user_agent: str = None, member_cid: str = None):
"""Class constructor.
Initializes the base class by ingesting credentials,
Expand All @@ -73,17 +73,23 @@ def __init__(self: object, base_url: str = "https://api.crowdstrike.com",
creds -- Dictionary containing CrowdStrike API credentials.
Mutually exclusive to client_id / client_secret.
client_id -- Client ID for the CrowdStrike API. Mutually exclusive to creds.
client_secret -- Client Secret for the CrowdStriek API. Mutually exclusive to creds.
client_secret -- Client Secret for the CrowdStrike API. Mutually exclusive to creds.
member_cid -- Child CID to connec to. Mutually exclusive to creds.
This method only accepts keywords to specify arguments.
This method only supports keywords to specify arguments.
"""
if client_id and client_secret and not creds:
creds = {
"client_id": client_id,
"client_secret": client_secret
}
# Have to pass member_cid the same way you pass client_id / secret
# If you use a creds dictionary, pass the member_cid there instead
if member_cid:
creds["member_cid"] = member_cid
elif not creds:
creds = {}

self.creds = creds
self.base_url = confirm_base_url(base_url)
self.ssl_verify = ssl_verify
Expand Down
41 changes: 29 additions & 12 deletions tests/test_authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ def uberAuth(self):
else:
return False

def failUberMSSPAuth(self):
status = self.getConfig()
if status:
self.falcon = APIHarness(client_id=self.config["falcon_client_id"],
client_secret=self.config["falcon_client_secret"],
member_cid="1234567890ABCDEFG"
)
self.falcon.authenticate()
if not self.falcon.authenticated:
return True
else:
return False
else:
return False

def uberRevoke(self):
return self.falcon.deauthenticate()

Expand Down Expand Up @@ -142,11 +157,10 @@ def serviceMSSPAuth(self):
status = self.getConfig()
result = False
if status:
authorization = OAuth2(creds={
'client_id': self.config["falcon_client_id"],
'client_secret': self.config["falcon_client_secret"],
'member_cid': '1234567890ABCDEFG'
})
authorization = OAuth2(client_id=self.config["falcon_client_id"],
client_secret=self.config["falcon_client_secret"],
member_cid='1234567890ABCDEFG'
)
try:
req = authorization.token()
if req["status_code"] in [201, 403]: # Prolly an invalid MSSP cred, 403 is correct
Expand All @@ -157,17 +171,17 @@ def serviceMSSPAuth(self):
return result

def failServiceAuth(self):
self.authorization = OAuth2(creds={
'client_id': "BadClientID",
'client_secret': "BadClientSecret"
})
self.authorization.base_url = "nowhere"
self.authorization = Hosts(client_id="BadClientID",
client_secret="BadClientSecret",
member_cid="123456789ABCDEFG"
)
self.authorization.auth_object.base_url = "nowhere"
try:
self.token = self.authorization.token()['body']['access_token']
self.token = self.authorization.auth_object.token()['body']['access_token']
except KeyError:
self.token = False

self.authorization.revoke(self.token)
self.authorization.auth_object.revoke(self.token)

if self.token:
return False
Expand Down Expand Up @@ -210,6 +224,9 @@ def test_serviceAuthNoSSL(self):
def test_serviceMSSPAuth(self):
assert self.serviceMSSPAuth() is True

def test_uberMSSPAuthFailure(self):
assert self.failUberMSSPAuth() is True

def test_serviceRevoke(self):
self.serviceAuth()
assert self.serviceRevoke() is True
Expand Down

0 comments on commit e3bfd9a

Please sign in to comment.