Skip to content

Commit

Permalink
Feature/exceptions - INTEG 885 (#41)
Browse files Browse the repository at this point in the history
Co-authored-by: Juliya Smith <juliya.smith@code42.com>
  • Loading branch information
kiran-chaudhary and Juliya Smith committed Mar 24, 2020
1 parent 82e74df commit a49165b
Show file tree
Hide file tree
Showing 17 changed files with 280 additions and 171 deletions.
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
The intended audience of this file is for py42 consumers -- as such, changes that don't affect
how a consumer would use the library (e.g. adding unit tests, updating documentation, etc) are not captured here.

## Unreleased

### Added

- py42 specific exceptions at new module `py42.sdk.exceptions`:
- `Py42Error`
- `Py42ArchiveFileNotFoundError`
- `Py42SessionInitializationError`
- `Py42FeatureUnavailableError`
- `Py42SecurityPlanConnectionError`
- `Py42HTTPError`
- `Py42BadRequestError`
- `Py42UnauthorizedError`
- `Py42ForbiddenError`
- `Py42NotFoundError`
- `Py42InternalServerError`

### Changed

- `py42.sdk.archive.stream_from_backup()` now raises `Py42ArchiveFileNotFoundError` when it does not find a file.
- `py42.sdk.alerts` and `py42.sdk.detectionlists` raise `Py42SessionInitializationError` if they are unable to
connect to the necessary microservice and `Py42FeatureUnavailableError` if their environment does not support
the microservice.
- `py42.sdk.securitydata.get_security_plan_storage_info_list()` raises `Py42SecurityPlanConnectionError` if it can't
connect to get plan info.
- Storage node connection issues may raise `Py42StorageSessionInitializationError`.
- All requests may raise a subclass of `Py42HTTPError` denoting which type of HTTP error it is:
- `Py42BadRequestError`
- `Py42UnauthorizedError`
- `Py42ForbiddenError`
- `Py42NotFoundError`
- `Py42InternalServerError`

## 0.6.1 - 2020-03-17

### Changed
Expand Down
14 changes: 4 additions & 10 deletions src/py42/_internal/archive_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import time
from collections import namedtuple

from py42.sdk.exceptions import Py42ArchiveFileNotFoundError

FileSelection = namedtuple(u"FileSelection", u"path_set, num_files, num_dirs, size")


Expand Down Expand Up @@ -64,11 +66,7 @@ def _get_file_via_walking_tree(self, file_path):
if root[u"path"].lower() == path_root.lower():
return self._walk_tree(root, path_parts[1:])

raise Exception(
u"File not found in archive for device {0} at path {1}".format(
self._device_guid, file_path
)
)
raise Py42ArchiveFileNotFoundError(self._device_guid, file_path)

def _walk_tree(self, current_node, remaining_path_components):
if not remaining_path_components or not remaining_path_components[0]:
Expand All @@ -82,11 +80,7 @@ def _walk_tree(self, current_node, remaining_path_components):
if child[u"path"].lower() == target_child_path.lower():
return self._walk_tree(child, remaining_path_components[1:])

raise Exception(
u"File not found in archive for device {0} at path {1}".format(
self._device_guid, target_child_path
)
)
raise Py42ArchiveFileNotFoundError(self._device_guid, target_child_path)

def _get_children(self, node_id=None):
return self._storage_archive_client.get_file_path_metadata(
Expand Down
21 changes: 3 additions & 18 deletions src/py42/_internal/auth_handling.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from py42._internal.compat import str


class TokenProvider(object):
def get_secret_value(self, force_refresh=False):
pass
Expand All @@ -12,15 +9,8 @@ def __init__(self, token_provider, session_modifier):
self._session_modifier = session_modifier

def renew_authentication(self, session, use_cache=False):
try:
secret = self._token_provider.get_secret_value(force_refresh=not use_cache)
self._session_modifier.modify_session(session, secret)
except Exception as ex:
message = (
u"An error occurred while trying to handle an unauthorized request, caused by: {0}"
)
message = message.format(str(ex))
raise Exception(message)
secret = self._token_provider.get_secret_value(force_refresh=not use_cache)
self._session_modifier.modify_session(session, secret)

@staticmethod
def response_indicates_unauthorized(response):
Expand All @@ -33,9 +23,4 @@ def __init__(self, header_name=u"Authorization", value_format=u"{0}"):
self._value_format = value_format

def modify_session(self, session, value):
try:
session.headers.update({self._header_name: self._value_format.format(value)})
except Exception as ex:
message = u"An error occurred while trying to apply a {0} header to the session, caused by: {1}"
message = message.format(self._header_name, str(ex))
raise Exception(message)
session.headers.update({self._header_name: self._value_format.format(value)})
17 changes: 9 additions & 8 deletions src/py42/_internal/client_factories.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from requests import HTTPError

from py42._internal.key_value_store import KeyValueStoreClient
from py42.clients import (
Expand All @@ -11,8 +12,13 @@
securitydata,
users,
)

from py42.clients.detectionlists.departing_employee import DepartingEmployeeClient
from py42.clients.file_event import FileEventClient
from py42.sdk.exceptions import (
Py42FeatureUnavailableError,
Py42SessionInitializationError,
)


class AuthorityClientFactory(object):
Expand Down Expand Up @@ -98,18 +104,13 @@ def _get_sts_base_url(session):
uri = u"/api/ServerEnv"
try:
response = session.get(uri)
except Exception as ex:
message = (
u"An error occurred while requesting server environment information, caused by {0}"
)
message = message.format(ex)
raise Exception(message)
except HTTPError as ex:
raise Py42SessionInitializationError(ex)

sts_base_url = None
if response.text:
response_json = json.loads(response.text)
sts_base_url = response_json.get(u"stsBaseUrl")
if not sts_base_url:
message = u"You may be trying to use a feature that is unavailable in your environment."
raise Exception(u"stsBaseUrl not found. {0}".format(message))
raise Py42FeatureUnavailableError()
return sts_base_url
15 changes: 3 additions & 12 deletions src/py42/_internal/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def __init__(self, host_address, session_factory, root_session):
archive_locator_factory = StorageTokenProviderFactory(
self.session, self.security_client, self.device_client
)

self.storage_client_factory = _get_storage_client_factory(
session_factory, archive_locator_factory
)
Expand Down Expand Up @@ -59,15 +60,5 @@ def _set_v3_session(self, host_address, session_factory, root_session):

@staticmethod
def _test_session(session, test_uri):
try:
response = session.get(test_uri)
return 200 <= response.status_code < 300
except Exception:
message = (
u"Invalid credentials or host address ({0}). Check that the username and password are correct, that the "
u"host is available and reachable, and that you have supplied the full scheme, domain, and port "
u"(e.g. https://myhost.code42.com:4285). If you are using a self-signed ssl certificate, try setting "
u"py42.sdk.settings.verify_ssl_certs to false (or using a cert from a legitimate certificate "
u"authority).".format(session.host_address)
)
raise Exception(message)
response = session.get(test_uri)
return 200 <= response.status_code < 300
7 changes: 3 additions & 4 deletions src/py42/_internal/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from threading import Lock

import requests.adapters

from py42._internal.compat import str, urljoin, urlparse
from py42.sdk.response import Py42Response
from py42.sdk.exceptions import raise_py42_error


class Py42Session(object):
Expand Down Expand Up @@ -108,9 +108,8 @@ def request(self, method, url, **kwargs):
response.encoding = u"utf-8" # setting this manually speeds up read times

return Py42Response(response)

except (requests.HTTPError, requests.RequestException, Exception) as ex:
raise ex
except requests.HTTPError as err:
raise_py42_error(err)

def _try_make_request(
self,
Expand Down
6 changes: 4 additions & 2 deletions src/py42/_internal/storage_session_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from threading import Lock
from requests import HTTPError

from py42.sdk.exceptions import Py42StorageSessionInitializationError
from py42._internal.compat import str


Expand All @@ -22,9 +24,9 @@ def get_storage_session(self, token_provider):
if session is None:
session = self.create_storage_session(url, token_provider)
self._session_cache.update({url.lower(): session})
except Exception as ex:
except HTTPError as ex:
message = u"Failed to create or retrieve session, caused by: {0}".format(str(ex))
raise Exception(message)
raise Py42StorageSessionInitializationError(message)
return session

def create_storage_session(self, url, token_provider):
Expand Down
96 changes: 29 additions & 67 deletions src/py42/_internal/token_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import json

from py42._internal.auth_handling import TokenProvider
from py42._internal.compat import str

V3_AUTH = u"v3_user_token"

Expand All @@ -25,14 +24,8 @@ def __init__(self, auth_session):
def get_secret_value(self, force_refresh=False):
uri = u"/c42api/v3/auth/jwt"
params = {u"useBody": True}
try:
response = self._auth_session.get(uri, params=params)
token = response[V3_AUTH]
return token
except Exception as ex:
message = u"An error occurred while trying to retrieve a jwt token, caused by {0}"
message = message.format(str(ex))
raise Exception(message)
response = self._auth_session.get(uri, params=params)
return response[V3_AUTH]


class C42ApiV1TokenProvider(TokenProvider):
Expand All @@ -42,14 +35,8 @@ def __init__(self, auth_session):

def get_secret_value(self, force_refresh=False):
uri = u"/api/AuthToken"
try:
response = self._auth_session.post(uri, data=None)
token = u"{0}-{1}".format(response[0], response[1])
return token
except Exception as ex:
message = u"An error occurred while trying to retrieve a V1 auth token, caused by {0}"
message = message.format(str(ex))
raise Exception(message)
response = self._auth_session.post(uri, data=None)
return u"{0}-{1}".format(response[0], response[1])


class C42APITmpAuthProvider(TokenProvider):
Expand All @@ -58,17 +45,10 @@ def __init__(self):
self._cached_info = None

def get_login_info(self):
try:
if self._cached_info is None:
response = self.get_tmp_auth_token() # pylint: disable=assignment-from-no-return
self._cached_info = response
return self._cached_info
except Exception as ex:
message = (
u"An error occurred while trying to retrieve storage logon info, caused by {0}"
)
message = message.format(str(ex))
raise Exception(message)
if self._cached_info is None:
response = self.get_tmp_auth_token() # pylint: disable=assignment-from-no-return
self._cached_info = response
return self._cached_info

def get_tmp_auth_token(self):
pass
Expand All @@ -88,20 +68,14 @@ def __init__(self, auth_session, user_id, device_guid, destination_guid):
self._destination_guid = destination_guid

def get_tmp_auth_token(self):
try:
uri = u"/api/LoginToken"
data = {
u"userId": self._user_id,
u"sourceGuid": self._device_guid,
u"destinationGuid": self._destination_guid,
}
response = self._auth_session.post(uri, data=json.dumps(data))
return response
except Exception as ex:
message = u"An error occurred while requesting a LoginToken, caused by {0}"
message = message.format(str(ex))
raise Exception(message)

uri = u"/api/LoginToken"
data = {
u"userId": self._user_id,
u"sourceGuid": self._device_guid,
u"destinationGuid": self._destination_guid,
}
response = self._auth_session.post(uri, data=json.dumps(data))
return response

class C42APIStorageAuthTokenProvider(C42APITmpAuthProvider):
def __init__(self, auth_session, plan_uid, destination_guid):
Expand All @@ -111,15 +85,10 @@ def __init__(self, auth_session, plan_uid, destination_guid):
self._destination_guid = destination_guid

def get_tmp_auth_token(self):
try:
uri = u"/api/StorageAuthToken"
data = {u"planUid": self._plan_uid, u"destinationGuid": self._destination_guid}
response = self._auth_session.post(uri, data=json.dumps(data))
return response
except Exception as ex:
message = u"An error occurred while requesting a StorageAuthToken, caused by {0}"
message = message.format(str(ex))
raise Exception(message)
uri = u"/api/StorageAuthToken"
data = {u"planUid": self._plan_uid, u"destinationGuid": self._destination_guid}
response = self._auth_session.post(uri, data=json.dumps(data))
return response


class StorageTokenProviderFactory(object):
Expand All @@ -132,22 +101,15 @@ def create_security_archive_locator(self, plan_uid, destination_guid):
return C42APIStorageAuthTokenProvider(self._auth_session, plan_uid, destination_guid)

def create_backup_archive_locator(self, device_guid, destination_guid=None):
try:
if destination_guid is None:
response = self._device_client.get_by_guid(device_guid, include_backup_usage=True)
if destination_guid is None:
response = self._device_client.get_by_guid(device_guid, include_backup_usage=True)
if destination_guid is None:
# take the first destination guid we find
destination_list = response["backupUsage"]
if not destination_list:
raise Exception(
u"No destinations found for device guid: {0}".format(device_guid)
)
destination_guid = destination_list[0][u"targetComputerGuid"]
except Exception as ex:
message = (
u"An error occurred while trying to determine a destination for device guid: {0},"
u" caused by: {1}".format(device_guid, str(ex))
)
raise Exception(message)
# take the first destination guid we find
destination_list = response["backupUsage"]
if not destination_list:
raise Exception(
u"No destinations found for device guid: {0}".format(device_guid)
)
destination_guid = destination_list[0][u"targetComputerGuid"]

return C42APILoginTokenProvider(self._auth_session, u"my", device_guid, destination_guid)

0 comments on commit a49165b

Please sign in to comment.