Skip to content

Feature/multiple profiles #12

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 68 commits into from
Mar 12, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
618d899
Rename section name
Mar 9, 2020
65ea9cd
Working
Mar 10, 2020
85e3ceb
Make showing profile work
Mar 10, 2020
a3e5961
List
Mar 10, 2020
8af84ed
Format
Mar 10, 2020
f25b0c8
Make work
Mar 10, 2020
d13ad88
Test profile
Mar 10, 2020
0b12a7e
Tests pas
Mar 10, 2020
2baf042
Update doc
Mar 10, 2020
25d88ee
Update doc
Mar 10, 2020
0b20d04
Format?
Mar 10, 2020
e2cdfb4
Use use
Mar 10, 2020
aec835e
Update readme:
Mar 10, 2020
f6ebfa7
I forgot what was this commit is for
Mar 11, 2020
e90bd27
Oh ya
Mar 11, 2020
695124d
Refactor
Mar 11, 2020
2b9ad29
Fix a test
Mar 11, 2020
e496129
All tests pass exclude config tests
Mar 11, 2020
98703f4
Write some config tests that I actually like
Mar 11, 2020
b0242cc
Fix show
Mar 11, 2020
a31b07e
Made switch actually work
Mar 11, 2020
2880d75
Fix merge flicts
Mar 11, 2020
be1c501
Prevent saving config file during tests
Mar 11, 2020
ef04e58
Remove flicts from cl
Mar 11, 2020
396d454
Update cl
Mar 11, 2020
a3a4bf0
Fix readme
Mar 11, 2020
fe78da1
Prevent db from being created in tests
Mar 11, 2020
3fd91a2
Support list search filters
Mar 11, 2020
ad44063
Remove unused const
Mar 11, 2020
331e7a2
Take a profile for reset pw
Mar 11, 2020
0d1cbf9
Add docstrings
Mar 11, 2020
54b9824
More tests
Mar 11, 2020
af9e43f
Fix advanced query
Mar 11, 2020
2015cea
Mention wildcards in readme
Mar 11, 2020
298d047
Remove autouse
Mar 11, 2020
d5964b4
Reset mock
Mar 11, 2020
cf0effa
Just check that save is called
Mar 11, 2020
0a1598d
Fix cursor store so it supports multiple rows
Mar 11, 2020
5cd88c8
Update docstrs
Mar 11, 2020
478c4d2
Update docstrs
Mar 11, 2020
7983961
Missing u
Mar 11, 2020
6f32070
Change method name
Mar 11, 2020
da8c602
Update changelog
Mar 12, 2020
dcbc30b
Made internal internal
Mar 12, 2020
1b9a709
Fix docstr
Mar 12, 2020
6744fd1
Fix docstrs
Mar 12, 2020
3f44734
Fix docstrs
Mar 12, 2020
7bf30b7
Add test
Mar 12, 2020
e9e3413
Fix cl
Mar 12, 2020
5f3e658
Update readme
Mar 12, 2020
979c918
Update readme
Mar 12, 2020
1704072
Update readme
Mar 12, 2020
5058bfb
Merge master
Mar 12, 2020
ed873a3
Strip and format
Mar 12, 2020
f3adb3c
mention format in readme
Mar 12, 2020
451df3b
Mention dev unrecommended ignore of ssl errors
Mar 12, 2020
3be7c91
Strip at the right spot
Mar 12, 2020
886e2e9
missing us
Mar 12, 2020
4bb9c7e
Delete space
Mar 12, 2020
b7c0bc1
Optimize imports
Mar 12, 2020
3236748
Update CHANGELOG.md
alanag13 Mar 12, 2020
857a651
Fix missing values exit
Mar 12, 2020
600a8b3
Merge branch 'feature/multiple-profiles' of https://github.com/code42…
Mar 12, 2020
bde152e
Fix issue where clearing checkpoint deleted everyones checkpoint
Mar 12, 2020
f34ad14
Create filters before sdk to validate args sooner
Mar 12, 2020
a2f001c
Reorganize extraction
Mar 12, 2020
351f5df
Bumps
Mar 12, 2020
44e049f
Print help messagE
Mar 12, 2020
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# The Code42 CLI

Use the `code42` command to interact with your Code42 environment.
`code42 securitydata` is a CLI tool for extracting AED events.
Additionally, `code42 securitydata` can record a checkpoint so that you only get events you have not previously gotten.
`code42 securitydata` is a CLI tool for extracting file events.
Additionally, you can choose to only get events that Code42 previously did not observe since you last recorded a checkpoint.

## Requirements

Expand Down
25 changes: 25 additions & 0 deletions src/code42cli/arguments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
PROFILE_NAME_KEY = u"profile_name"


def add_arguments_to_parser(parser):
add_debug_arg(parser)
add_profile_name_arg(parser)


def add_debug_arg(parser):
parser.add_argument(
u"-d",
u"--debug",
dest=u"is_debug_mode",
action=u"store_true",
help=u"Turn on Debug logging.",
)


def add_profile_name_arg(parser):
parser.add_argument(
u"--profile",
action=u"store",
dest=PROFILE_NAME_KEY,
help=u"The name of the profile containing your Code42 username and authority host address.",
)
205 changes: 129 additions & 76 deletions src/code42cli/profile/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,136 +6,189 @@
import code42cli.util as util
from code42cli.compat import str

_DEFAULT_VALUE = u"__DEFAULT__"
DEFAULT_VALUE = u"__DEFAULT__"
_PROFILE_ENV_VAR_NAME = u"CODE42CLI_PROFILE"


class ConfigurationKeys(object):
USER_SECTION = u"Code42"
AUTHORITY_KEY = u"c42_authority_url"
USERNAME_KEY = u"c42_username"
IGNORE_SSL_ERRORS_KEY = u"ignore-ssl-errors"
INTERNAL_SECTION = u"Internal"
HAS_SET_PROFILE_KEY = u"has_set_profile"
DEFAULT_PROFILE_IS_COMPLETE = u"default_profile_is_complete"
DEFAULT_PROFILE = u"default_profile"


def get_config_profile():
"""Get your config file profile."""
def get_profile(profile_name=None):
"""Gets the config file variables for the given name.

Args:
profile_name: The name of the profile to get variables for.
If None, uses the default profile name.
"""
parser = ConfigParser()
if not profile_has_been_set():
util.print_error(u"Profile has not completed setup.")
print(u"")
print(u"To set, use: ")
util.print_bold(u"\tcode42 profile set -s <authority-URL> -u <username>")
print(u"")
available_profiles = _get_all_profile_names()
if len(available_profiles) == 0:
util.print_no_existing_profile_message()
exit(1)

return _get_config_profile_from_parser(parser)
if profile_name and profile_name not in available_profiles:
_print_profile_not_exists_message(profile_name)
exit(1)

if not profile_name:
profile_name = _get_default_profile_name()

def profile_has_been_set():
"""Whether you have, at one point in time, set your username and authority server URL."""
parser = ConfigParser()
config_file_path = _get_config_file_path()
parser.read(config_file_path)
settings = parser[ConfigurationKeys.INTERNAL_SECTION]
return settings.getboolean(ConfigurationKeys.HAS_SET_PROFILE_KEY)
return _get_profile_from_parser(parser, profile_name)


def mark_as_set_if_complete():
if not _profile_can_be_set():
return
parser = ConfigParser()
config_file_path = _get_config_file_path()
parser.read(config_file_path)
settings = parser[ConfigurationKeys.INTERNAL_SECTION]
settings[ConfigurationKeys.HAS_SET_PROFILE_KEY] = u"True"
_save(parser, ConfigurationKeys.HAS_SET_PROFILE_KEY)
def get_all_profiles():
names = _get_all_profile_names()
profiles = []
for name in names:
profiles.append(get_profile(name))
return profiles


def set_username(new_username):
def write_username(new_username, profile_name=None):
profile_name = profile_name or _get_default_profile_name()
parser = ConfigParser()
profile = _get_config_profile_from_parser(parser)
profile = _get_profile_from_parser(parser, profile_name)
profile[ConfigurationKeys.USERNAME_KEY] = new_username
_save(parser, ConfigurationKeys.USERNAME_KEY)
_save(parser, profile_name, ConfigurationKeys.USERNAME_KEY)
_try_mark_setup_as_complete(profile_name)


def set_authority_url(new_url):
def write_authority_url(new_url, profile_name=None):
profile_name = profile_name or _get_default_profile_name()
parser = ConfigParser()
profile = _get_config_profile_from_parser(parser)
profile = _get_profile_from_parser(parser, profile_name)
profile[ConfigurationKeys.AUTHORITY_KEY] = new_url
_save(parser, ConfigurationKeys.AUTHORITY_KEY)
_save(parser, profile_name, ConfigurationKeys.AUTHORITY_KEY)
_try_mark_setup_as_complete(profile_name)


def set_ignore_ssl_errors(new_value):
def write_ignore_ssl_errors(new_value, profile_name=None):
profile_name = profile_name or _get_default_profile_name()
parser = ConfigParser()
profile = _get_config_profile_from_parser(parser)
profile = _get_profile_from_parser(parser, profile_name)
profile[ConfigurationKeys.IGNORE_SSL_ERRORS_KEY] = str(new_value)
_save(parser, ConfigurationKeys.IGNORE_SSL_ERRORS_KEY)
_save(parser, profile_name, ConfigurationKeys.IGNORE_SSL_ERRORS_KEY)


def _profile_can_be_set():
"""Whether your current username and authority URL are set,
but your profile has not been marked as set.
"""
def _get_all_profile_names():
parser = ConfigParser()
profile = _get_config_profile_from_parser(parser)
username = profile[ConfigurationKeys.USERNAME_KEY]
authority = profile[ConfigurationKeys.AUTHORITY_KEY]
return username != _DEFAULT_VALUE and authority != _DEFAULT_VALUE and not profile_has_been_set()
_attach_config_file_to_profile(parser)
sections = list(parser.sections())
if ConfigurationKeys.INTERNAL_SECTION in sections:
sections.remove(ConfigurationKeys.INTERNAL_SECTION)
return sections


def _get_config_profile_from_parser(parser):
config_file_path = _get_config_file_path()
def _attach_config_file_to_profile(parser, profile_name=None):
config_file_path = _get_config_file_path(profile_name)
parser.read(config_file_path)
config = parser[ConfigurationKeys.USER_SECTION]
return config


def _get_config_file_path():
def _get_config_file_path(profile_name=None):
path = u"{}config.cfg".format(util.get_user_project_path())
if not os.path.exists(path) or not _verify_config_file(path):
_create_new_config_file(path)
if not os.path.exists(path):
_create_new_config_file(path, profile_name)
return path


def _create_new_config_file(path):
def _create_new_config_file(path, profile_name):
profile_name = profile_name or DEFAULT_VALUE
config_parser = ConfigParser()
config_parser = _create_user_section(config_parser)
config_parser = _create_internal_section(config_parser)
_save(config_parser, None, path)
if profile_name != DEFAULT_VALUE:
config_parser = _create_profile_section(config_parser, profile_name)
_save(config_parser, profile_name, None, path)


def _create_user_section(parser):
def _create_internal_section(parser):
keys = ConfigurationKeys
parser.add_section(keys.USER_SECTION)
parser[keys.USER_SECTION] = {}
parser[keys.USER_SECTION][keys.AUTHORITY_KEY] = _DEFAULT_VALUE
parser[keys.USER_SECTION][keys.USERNAME_KEY] = _DEFAULT_VALUE
parser[keys.USER_SECTION][keys.IGNORE_SSL_ERRORS_KEY] = u"False"
parser.add_section(keys.INTERNAL_SECTION)
parser[keys.INTERNAL_SECTION] = {}
parser[keys.INTERNAL_SECTION][keys.DEFAULT_PROFILE_IS_COMPLETE] = str(False)
parser[keys.INTERNAL_SECTION][keys.DEFAULT_PROFILE] = DEFAULT_VALUE
return parser


def _create_internal_section(parser):
def _get_default_profile_name():
parser = ConfigParser()
_attach_config_file_to_profile(parser)
return parser[ConfigurationKeys.INTERNAL_SECTION][ConfigurationKeys.DEFAULT_PROFILE]


def _print_profile_not_exists_message(profile_name):
util.print_error(u"Profile '{0}' does not exist.".format(profile_name))
util.print_set_profile_help()


def _get_profile_from_parser(parser, profile_name):
_attach_config_file_to_profile(parser, profile_name)
if profile_name not in parser.sections():
_create_profile_section(parser, profile_name)

return parser[profile_name]


def _create_profile_section(parser, profile_name):
keys = ConfigurationKeys
parser.add_section(keys.INTERNAL_SECTION)
parser[keys.INTERNAL_SECTION] = {}
parser[keys.INTERNAL_SECTION][keys.HAS_SET_PROFILE_KEY] = u"False"
parser.add_section(profile_name)
parser[profile_name] = {}
parser[profile_name][keys.AUTHORITY_KEY] = DEFAULT_VALUE
parser[profile_name][keys.USERNAME_KEY] = DEFAULT_VALUE
parser[profile_name][keys.IGNORE_SSL_ERRORS_KEY] = str(False)

default_profile = parser[keys.INTERNAL_SECTION].get(keys.DEFAULT_PROFILE)
if default_profile is None or default_profile is DEFAULT_VALUE:
parser[keys.INTERNAL_SECTION][keys.DEFAULT_PROFILE] = profile_name
return parser


def _save(parser, key=None, path=None):
path = _get_config_file_path() if path is None else path
def _try_mark_setup_as_complete(profile_name):
if not _setup_ready_for_completion(profile_name):
return

keys = ConfigurationKeys
parser = ConfigParser()
config_file_path = _get_config_file_path(profile_name)
parser.read(config_file_path)
settings = parser[keys.INTERNAL_SECTION]
settings[keys.DEFAULT_PROFILE_IS_COMPLETE] = str(True)

default_profile = parser[keys.INTERNAL_SECTION].get(keys.DEFAULT_PROFILE)
if default_profile is None or default_profile is DEFAULT_VALUE:
parser[keys.INTERNAL_SECTION][keys.DEFAULT_PROFILE] = profile_name

_save(parser, profile_name, keys.DEFAULT_PROFILE_IS_COMPLETE)


def _setup_ready_for_completion(profile_name):
parser = ConfigParser()
profile = _get_profile_from_parser(parser, profile_name)
username = profile[ConfigurationKeys.USERNAME_KEY]
authority = profile[ConfigurationKeys.AUTHORITY_KEY]
username_exists = username is not None and username != DEFAULT_VALUE
authority_exists = authority is not None and authority != DEFAULT_VALUE
return username_exists and authority_exists and not _default_profile_exists()


def _default_profile_exists():
parser = ConfigParser()
config_file_path = _get_config_file_path()
parser.read(config_file_path)
settings = parser[ConfigurationKeys.INTERNAL_SECTION]
return settings.getboolean(ConfigurationKeys.DEFAULT_PROFILE_IS_COMPLETE)


def _save(parser, profile_name, key=None, path=None):
path = _get_config_file_path(profile_name) if path is None else path
util.open_file(path, u"w+", lambda f: parser.write(f))
if key is not None:
if key == ConfigurationKeys.HAS_SET_PROFILE_KEY:
if key == ConfigurationKeys.DEFAULT_PROFILE_IS_COMPLETE:
print(u"You have completed setting up your profile!")
else:
print(u"'{}' has been successfully updated".format(key))


def _verify_config_file(path):
keys = ConfigurationKeys
config_parser = ConfigParser()
config_parser.read(path)
sections = config_parser.sections()
return keys.USER_SECTION in sections and keys.INTERNAL_SECTION in sections
21 changes: 10 additions & 11 deletions src/code42cli/profile/password.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@
_ROOT_SERVICE_NAME = u"code42cli"


def get_password():
"""Gets your currently stored password for your username / authority URL combo."""
profile = config.get_config_profile()
service_name = _get_service_name(profile)
def get_password(profile_name):
"""Gets your currently stored password for your profile."""
profile = config.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():
"""Prompts and sets your password for your username / authority URL combo."""
def set_password_from_prompt(profile_name):
"""Prompts and sets your password for your profile."""
password = getpass()
profile = config.get_config_profile()
service_name = _get_service_name(profile)
profile = config.get_profile(profile_name)
service_name = _get_service_name(profile_name)
username = _get_username(profile)
keyring.set_password(service_name, username, password)
print(u"'Code42 Password' updated.")
Expand All @@ -34,9 +34,8 @@ def get_password_from_prompt():
return getpass()


def _get_service_name(profile):
authority_url = profile[ConfigurationKeys.AUTHORITY_KEY]
return u"{}::{}".format(_ROOT_SERVICE_NAME, authority_url)
def _get_service_name(profile_name):
return u"{}::{}".format(_ROOT_SERVICE_NAME, profile_name)


def _get_username(profile):
Expand Down
Loading