Skip to content

Bugfix/defaultp not used #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ 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.

## 0.4.1 - 2020-03-13

### Fixed

- Bug where `profile reset-pw` did not work with the default profile.
- Bug where `profile show` indicated a password was set for a different profile.
- We now validate credentials when setting a password.


### Changed

- Date inputs are now required to be in quotes when they include a time.

## 0.4.0 - 2020-03-12

### Added
Expand Down
2 changes: 1 addition & 1 deletion src/code42cli/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.0"
__version__ = "0.4.1"
32 changes: 17 additions & 15 deletions src/code42cli/profile/password.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,32 @@
_ROOT_SERVICE_NAME = u"code42cli"


def get_password(profile_name):
"""Gets your currently stored password for your profile."""
accessor = get_config_accessor()
profile = accessor.get_profile(profile_name)
service_name = _get_service_name(profile_name)
def get_stored_password(profile_name):
"""Gets your currently stored password for the given profile name."""
profile = _get_profile(profile_name)
service_name = _get_service_name(profile.name)
username = _get_username(profile)
password = keyring.get_password(service_name, username)
return password


def set_password_from_prompt(profile_name):
"""Prompts and sets your password for your profile."""
password = getpass()
accessor = get_config_accessor()
profile = accessor.get_profile(profile_name)
service_name = _get_service_name(profile_name)
def get_password_from_prompt():
"""Prompts you and returns what you input."""
return getpass()


def set_password(profile_name, new_password):
"""Sets your password for the given profile name."""
profile = _get_profile(profile_name)
service_name = _get_service_name(profile.name)
username = _get_username(profile)
keyring.set_password(service_name, username, password)
keyring.set_password(service_name, username, new_password)
print(u"'Code42 Password' updated.")
return password


def get_password_from_prompt():
return getpass()
def _get_profile(profile_name):
accessor = get_config_accessor()
return accessor.get_profile(profile_name)


def _get_service_name(profile_name):
Expand Down
26 changes: 18 additions & 8 deletions src/code42cli/profile/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
print_set_profile_help,
print_no_existing_profile_message,
)
from code42cli.sdk_client import validate_connection


class Code42Profile(object):
Expand All @@ -33,7 +34,7 @@ def ignore_ssl_error(self):
return self._profile[ConfigAccessor.IGNORE_SSL_ERRORS_KEY]

def get_password(self):
pwd = password.get_password(self.name)
pwd = password.get_stored_password(self.name)
if not pwd:
pwd = password.get_password_from_prompt()
return pwd
Expand Down Expand Up @@ -91,7 +92,7 @@ def show_profile(args):
print(u"\t* {0} = {1}".format(ConfigAccessor.USERNAME_KEY, profile.username))
print(u"\t* {0} = {1}".format(ConfigAccessor.AUTHORITY_KEY, profile.authority_url))
print(u"\t* {0} = {1}".format(ConfigAccessor.IGNORE_SSL_ERRORS_KEY, profile.ignore_ssl_error))
if password.get_password(args.profile_name) is not None:
if password.get_stored_password(profile.name) is not None:
print(u"\t* A password is set.")
print(u"")

Expand All @@ -109,7 +110,15 @@ def set_profile(args):

def prompt_for_password_reset(args):
"""Securely prompts for your password and then stores it using keyring."""
password.set_password_from_prompt(args.profile_name)
profile = get_profile(args.profile_name)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will get the default profile if args.profile_name is None. Thus, profile.name will not be None anymore.

new_password = password.get_password_from_prompt()
if not validate_connection(profile.authority_url, profile.username, new_password):
print_error(
"Your password was not saved because your credentials failed to validate. "
"Check your network connection and the spelling of your username and server URL."
)
exit(1)
password.set_password(profile.name, new_password)


def list_profiles(*args):
Expand All @@ -130,7 +139,7 @@ def use_profile(args):
try:
accessor.switch_default_profile(args.profile_name)
except Exception as ex:
print_error(ex)
print_error(str(ex))
exit(1)


Expand Down Expand Up @@ -198,9 +207,10 @@ def _verify_args_for_set(args):
missing_values = not args.c42_username and not args.c42_authority_url
if missing_values:
try:
profile = get_profile(args.profile_name)
accessor = get_config_accessor()
Copy link
Contributor Author

@antazoey antazoey Mar 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanag13 This fixed an issue where you would get two error messages when only one made sense during set.. kinda weird to be catching a SystemExit too.

profile = Code42Profile(accessor.get_profile(args.profile_name))
missing_values = not profile.username and not profile.authority_url
except SystemExit:
except Exception:
missing_values = True

if missing_values:
Expand Down Expand Up @@ -231,10 +241,10 @@ def _missing_default_profile(args):
profile_name_arg_is_none = (
args.profile_name is None or args.profile_name == ConfigAccessor.DEFAULT_VALUE
)
return profile_name_arg_is_none and not _default_profile_exists()
return profile_name_arg_is_none and not _default_profile_exist()


def _default_profile_exists():
def _default_profile_exist():
try:
accessor = get_config_accessor()
profile = Code42Profile(accessor.get_profile())
Expand Down
28 changes: 28 additions & 0 deletions src/code42cli/sdk_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from py42 import debug_level
from py42 import settings
from py42.sdk import SDK

from code42cli.util import print_error


def create_sdk(profile, is_debug_mode):
if is_debug_mode:
settings.debug_level = debug_level.DEBUG
try:
password = profile.get_password()
return SDK.create_using_local_account(profile.authority_url, profile.username, password)
except Exception:
print_error(
u"Invalid credentials or host address. "
u"Verify your profile is set up correctly and that you are supplying the correct password."
)
exit(1)


def validate_connection(authority_url, username, password):
try:
SDK.create_using_local_account(authority_url, username, password)
return True
except:
print(username, password, authority_url)
return False
2 changes: 0 additions & 2 deletions src/code42cli/securitydata/arguments/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ def _add_begin_date_arg(parser):
parser.add_argument(
u"-b",
u"--begin",
nargs=u"+",
action=u"store",
dest=SearchArguments.BEGIN_DATE,
help=u"The beginning of the date range in which to look for events, "
Expand All @@ -82,7 +81,6 @@ def _add_end_date_arg(parser):
parser.add_argument(
u"-e",
u"--end",
nargs=u"+",
action=u"store",
dest=SearchArguments.END_DATE,
help=u"The end of the date range in which to look for events, "
Expand Down
18 changes: 9 additions & 9 deletions src/code42cli/securitydata/date_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,24 @@ def _verify_timestamp_order(min_timestamp, max_timestamp):
raise ValueError(u"Begin date cannot be after end date")


def _parse_timestamp(date_tuple):
def _parse_timestamp(date_and_time):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this method only get called if a min or max timestamp is present? if not, it looks like there would be some problem if date_and_time was None

try:
date_str = _join_date_tuple(date_tuple)
date_format = u"%Y-%m-%d" if len(date_tuple) == 1 else u"%Y-%m-%d %H:%M:%S"
date_str = _join_date_and_time(date_and_time)
date_format = u"%Y-%m-%d" if len(date_and_time) == 1 else u"%Y-%m-%d %H:%M:%S"
time = datetime.strptime(date_str, date_format)
except ValueError:
raise ValueError(_FORMAT_VALUE_ERROR_MESSAGE)
return convert_datetime_to_timestamp(time)


def _join_date_tuple(date_tuple):
if not date_tuple:
def _join_date_and_time(date_and_time):
if not date_and_time:
return None
date_str = date_tuple[0]
if len(date_tuple) == 1:
date_str = date_and_time[0]
if len(date_and_time) == 1:
return date_str
if len(date_tuple) == 2:
date_str = "{0} {1}".format(date_str, date_tuple[1])
if len(date_and_time) == 2:
date_str = "{0} {1}".format(date_str, date_and_time[1])
else:
raise ValueError(_FORMAT_VALUE_ERROR_MESSAGE)
return date_str
30 changes: 8 additions & 22 deletions src/code42cli/securitydata/extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

from c42eventextractor import FileEventHandlers
from c42eventextractor.extractors import FileEventExtractor
from py42 import debug_level
from py42 import settings
from py42.sdk import SDK
from py42.sdk.file_event_query.cloud_query import Actor
from py42.sdk.file_event_query.device_query import DeviceUsername
from py42.sdk.file_event_query.event_query import Source
Expand All @@ -22,6 +19,7 @@
from code42cli.securitydata.logger_factory import get_error_logger
from code42cli.securitydata.options import ExposureType as ExposureTypeOptions
from code42cli.util import print_error, print_bold, is_interactive
from code42cli.sdk_client import create_sdk

_EXCEPTIONS_OCCURRED = False

Expand All @@ -41,7 +39,7 @@ def extract(output_logger, args):
store = _create_cursor_store(args, profile)
filters = _get_filters(args, store)
handlers = _create_event_handlers(output_logger, store)
sdk = _get_sdk(profile, args.is_debug_mode)
sdk = create_sdk(profile, args.is_debug_mode)
extractor = FileEventExtractor(sdk, handlers)
_call_extract(extractor, filters, args)
_handle_result()
Expand Down Expand Up @@ -85,12 +83,12 @@ def _verify_begin_date_requirements(args, cursor_store):
def _begin_date_is_required(args, cursor_store):
if not args.is_incremental:
return True
required = cursor_store is not None and cursor_store.get_stored_insertion_timestamp() is None
is_required = cursor_store and cursor_store.get_stored_insertion_timestamp() is None

# Ignore begin date when is incremental mode, it is not required, and it was passed an argument.
if not required and args.begin_date:
if not is_required and args.begin_date:
args.begin_date = None
return required
return is_required


def _verify_exposure_types(exposure_types):
Expand Down Expand Up @@ -122,7 +120,9 @@ def _create_filters(args):

def _get_event_timestamp_filter(args):
try:
return date_helper.create_event_timestamp_filter(args.begin_date, args.end_date)
begin_date = args.begin_date.strip().split(" ") if args.begin_date else None
end_date = args.end_date.strip().split(" ") if args.end_date else None
return date_helper.create_event_timestamp_filter(begin_date, end_date)
except ValueError as ex:
print_error(str(ex))
exit(1)
Expand Down Expand Up @@ -153,20 +153,6 @@ def handle_response(response):
return handlers


def _get_sdk(profile, is_debug_mode):
if is_debug_mode:
settings.debug_level = debug_level.DEBUG
try:
password = profile.get_password()
return SDK.create_using_local_account(profile.authority_url, profile.username, password)
except Exception:
print_error(
u"Invalid credentials or host address. "
u"Verify your profile is set up correctly and that you are supplying the correct password."
)
exit(1)


def _call_extract(extractor, filters, args):
if args.advanced_query:
extractor.extract_advanced(args.advanced_query)
Expand Down
4 changes: 2 additions & 2 deletions src/code42cli/securitydata/logger_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def get_logger_for_file(filename, output_format):

with _logger_deps_lock:
if not _logger_has_handlers(logger):
handler = logging.FileHandler(filename, delay=True)
handler = logging.FileHandler(filename, delay=True, encoding="utf-8")
return _init_logger(logger, handler, output_format)
return logger

Expand Down Expand Up @@ -86,7 +86,7 @@ def get_error_logger():
with _logger_deps_lock:
if not _logger_has_handlers(logger):
formatter = logging.Formatter(u"%(asctime)s %(message)s")
handler = RotatingFileHandler(log_path, maxBytes=250000000)
handler = RotatingFileHandler(log_path, maxBytes=250000000, encoding="utf-8")
return _apply_logger_dependencies(logger, handler, formatter)
return logger

Expand Down
4 changes: 2 additions & 2 deletions src/code42cli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ def get_user_project_path(subdir=""):

def open_file(file_path, mode, action):
"""Wrapper for opening files, useful for testing purposes."""
with open(file_path, mode) as f:
with open(file_path, mode, encoding="utf-8") as f:
action(f)


def print_error(error_text):
"""Prints red text."""
print("\033[91mUSAGE ERROR: {}\033[0m".format(error_text))
print("\033[91mERROR: {}\033[0m".format(error_text))


def print_bold(bold_text):
Expand Down
43 changes: 43 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import pytest

from code42cli.profile.config import ConfigAccessor
from code42cli.profile.profile import Code42Profile


@pytest.fixture
def namespace(mocker):
Expand All @@ -28,6 +31,46 @@ def namespace(mocker):
return mock


def create_profile_values_dict(authority=None, username=None, ignore_ssl=False):
return {
ConfigAccessor.AUTHORITY_KEY: authority,
ConfigAccessor.USERNAME_KEY: username,
ConfigAccessor.IGNORE_SSL_ERRORS_KEY: ignore_ssl,
}


class MockSection(object):
def __init__(self, name="Test Profile Name", values_dict=None):
self.name = name
self.values_dict = values_dict or create_profile_values_dict()

def __getitem__(self, item):
return self.values_dict[item]

def __setitem__(self, key, value):
self.values_dict[key] = value

def get(self, item):
return self.values_dict.get(item)


def create_mock_profile(name=None):
profile_section = MockSection(name)
profile = Code42Profile(profile_section)

def mock_get_password():
return "Test Password"

profile.get_password = mock_get_password
return profile


def setup_mock_accessor(mock_accessor, name=None, values_dict=None):
profile_section = MockSection(name, values_dict)
mock_accessor.get_profile.return_value = profile_section
return mock_accessor


def get_filter_value_from_json(json, filter_index):
return json_module.loads(str(json))["filters"][filter_index]["value"]

Expand Down
Loading