Skip to content
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
2 changes: 1 addition & 1 deletion keepercli-package/mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
warn_no_return = False
files = src/
exclude = src/keepercli/import_plugins/lastpass_lib
python_version = 3.8
python_version = 3.10

[mypy-keepersdk.proto.*]
ignore_errors = True
Expand Down
43 changes: 27 additions & 16 deletions keepercli-package/src/keepercli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from .commands import base


def get_params_from_config(config_filename: Optional[str]=None) -> params.KeeperParams:
def get_keeper_config(config_filename: Optional[str]=None) -> params.KeeperConfig:
if os.getenv("KEEPER_COMMANDER_DEBUG"):
logging.getLogger().setLevel(logging.DEBUG)
logging.info('Debug ON')
Expand Down Expand Up @@ -67,9 +67,7 @@ def get_default_path() -> pathlib.Path:
except IOError as ioe:
logging.warning('Error: Unable to open config file %s: %s', config_filename, ioe)

context = params.KeeperParams(config_filename, config or {})
context.config_filename = config_filename
return context
return params.KeeperConfig(config_filename=config_filename, config=config or {})


def usage(message: str) -> None:
Expand Down Expand Up @@ -135,7 +133,9 @@ def welcome() -> None:
parser.add_argument('--unmask-all', action='store_true', help=unmask_help)
fail_on_throttle_help = 'Disable default client-side pausing of command execution and re-sending of requests upon ' \
'server-side throttling'
parser.add_argument('--fail-on-throttle', action='store_true', help=fail_on_throttle_help)
parser.add_argument('--fail-on-throttle', dest='fail_on_throttle', action='store_true', help=fail_on_throttle_help)
parser.add_argument('--skip-vault', dest='skip_vault', action='store_true', help='Skip loading vault')
parser.add_argument('--skip-enterprise', dest='skip_enterprise', action='store_true', help='Skip loading enterprise')
parser.add_argument('command', nargs='?', type=str, action='store', help='Command')
parser.add_argument('options', nargs='*', action='store', help='Options')
setattr(parser, 'error', usage)
Expand All @@ -147,44 +147,55 @@ def main():
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
opts, flags = parser.parse_known_args(sys.argv[1:])

context = get_params_from_config(opts.config)
app_config = get_keeper_config(opts.config)

if opts.batch_mode:
context.batch_mode = True
app_config.batch_mode = True

if opts.debug:
context.debug = opts.debug
app_config.debug = opts.debug

logging.getLogger().setLevel(logging.WARNING if context.batch_mode else logging.DEBUG if opts.debug else logging.INFO)
logging.getLogger().setLevel(logging.WARNING if app_config.batch_mode else logging.DEBUG if opts.debug else logging.INFO)

if opts.version:
print(f'Keeper Commander, version {__version__}')
return

if opts.unmask_all:
context.unmask_all = opts.unmask_all
app_config.unmask_all = opts.unmask_all

if opts.skip_vault:
app_config.skip_vault = True

if opts.skip_enterprise:
app_config.skip_enterprise = True

if opts.fail_on_throttle:
context.fail_on_throttle = opts.fail_on_throttle
app_config.fail_on_throttle = opts.fail_on_throttle

if opts.server:
app_config.server = opts.server

if opts.user:
app_config.username = opts.user

if opts.password:
context.password = opts.password
app_config.password = opts.password
else:
pwd = os.getenv('KEEPER_PASSWORD')
if pwd:
context.password = pwd
app_config.password = pwd

if not opts.command:
opts.command = 'shell'

if not context.batch_mode:
if not app_config.batch_mode:
welcome()
versioning.welcome_print_version()

commands = base.CliCommands()
register_commands.register_commands(commands)
r_code = cli.loop(context, commands)
context.clear_session()
r_code = cli.loop(app_config, commands)
sys.exit(r_code)


Expand Down
68 changes: 41 additions & 27 deletions keepercli-package/src/keepercli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from . import prompt_utils, api, autocomplete
from .commands import command_completer, base, command_history
from .helpers import report_utils
from .params import KeeperParams
from .params import KeeperParams, KeeperConfig
from keepersdk import constants
from keepersdk.vault import vault_utils

Expand Down Expand Up @@ -45,58 +45,64 @@ def do_command(command_line: str, context: KeeperParams, commands: base.CliComma
return command.execute_args(context, args.strip(), command=orig_cmd)
else:
display_command_help(commands)
return None


def loop(context: KeeperParams, commands: base.CliCommands):
def loop(keeper_config: KeeperConfig, commands: base.CliCommands):
prompt_session: Optional[PromptSession] = None
command_queue: List[str] = []
context_stack: List[KeeperParams] = []
context = KeeperParams(keeper_config)

def get_prompt() -> str:
if context.batch_mode:
if keeper_config.batch_mode:
return ''
if context.auth is None:
return 'Not logged in'
if context.vault is None:
return context.auth.auth_context.username

vault_data = context.vault.vault_data
folder_path = vault_data.root_folder.name
path = vault_utils.get_folder_path(vault_data, folder_uid=context.current_folder)
if path:
folder_path += '/' + path
if context.vault is not None:
vault_data = context.vault.vault_data
folder_path = vault_data.root_folder.name
path = vault_utils.get_folder_path(vault_data, folder_uid=context.current_folder)
if path:
folder_path += '/' + path

if len(folder_path) > 40:
folder_path = '...' + folder_path[-37:]
return folder_path
if len(folder_path) > 40:
folder_path = '...' + folder_path[-37:]
return folder_path
if context.enterprise_data is not None:
return context.enterprise_data.enterprise_info.enterprise_name

return context.auth.auth_context.username

logger = api.get_logger()

if not context.batch_mode:
if not keeper_config.batch_mode:
if sys.stdin.isatty() and sys.stdout.isatty():
from prompt_toolkit.enums import EditingMode
from prompt_toolkit.shortcuts import CompleteStyle
completer = command_completer.CommandCompleter(commands, autocomplete.standard_completer(context))
prompt_session = PromptSession(
multiline=False, editing_mode=EditingMode.VI, complete_style=CompleteStyle.MULTI_COLUMN,
multiline=False, editing_mode=EditingMode.EMACS, complete_style=CompleteStyle.MULTI_COLUMN,
complete_while_typing=False, completer=completer, auto_suggest=None, key_bindings=prompt_utils.kb,
enable_history_search=False, history=KeeperHistory())

if context.username:
if keeper_config.username:
options = '--resume-session'
if context.password:
options += ' --pass="{0}"'.format(context.password.replace('"', '\\"'))
cmd = 'login ' + options + ' ' + context.username
if keeper_config.password:
options += ' --pass="{0}"'.format(keeper_config.password.replace('"', '\\"'))
cmd = 'login ' + options + ' ' + keeper_config.username
command_queue.append(cmd)
else:
if context.server:
api.get_logger().info('Current Keeper region: %s', context.server)
if keeper_config.server:
api.get_logger().info('Current Keeper region: %s', keeper_config.server)
else:
api.get_logger().info('Use "server" command to change Keeper region > "server US"')
for region in constants.KEEPER_PUBLIC_HOSTS:
api.get_logger().info('\t%s: %s', region, constants.KEEPER_PUBLIC_HOSTS[region])
api.get_logger().info('To login type: login <email>')
else:
logger.setLevel(logging.DEBUG if context.debug else logging.WARNING)
logger.setLevel(logging.DEBUG if keeper_config.debug else logging.WARNING)

while True:
if context.auth:
Expand All @@ -123,21 +129,29 @@ def get_prompt() -> str:
continue

if command.lower() in ('q', 'quit'):
break
if len(context_stack) > 0:
context_to_release = context
context = context_stack.pop()
logger.info('Returning to previous context...')
context_to_release.clear_session()
continue
else:
break

suppress_errno = False
if command.startswith("@"):
suppress_errno = True
command = command[1:]
if context.batch_mode:
logger.info('> %s', command)
error_no = 1
try:
if context.vault and context.vault.sync_requested:
context.vault.sync_down()
result = do_command(command, context, commands)
error_no = 0
if result:
if isinstance(result, KeeperParams):
context_stack.append(context)
context = result
elif isinstance(result, str):
prompt_utils.output_text(result)
except base.CommandError as ce:
logger.warning(ce.message)
Expand All @@ -149,7 +163,7 @@ def get_prompt() -> str:
logger.debug(e, exc_info=True)
logger.error('An unexpected error occurred: %s. Type "debug" to toggle verbose error output', e)

if context.batch_mode and error_no != 0 and not suppress_errno:
if keeper_config.batch_mode and error_no != 0 and not suppress_errno:
break


Expand Down
27 changes: 15 additions & 12 deletions keepercli-package/src/keepercli/commands/account_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import datetime
from typing import Tuple, Optional, List, Any

from keepersdk.authentication import auth_utils
from keepersdk.authentication import keeper_auth
from keepersdk.proto import AccountSummary_pb2
from . import base
from .. import params, login, api
Expand All @@ -25,9 +25,12 @@ def execute(self, context: params.KeeperParams, **kwargs):
username = kwargs.get('email') or ''
password = kwargs.get('password') or ''
resume_session = kwargs.get('resume_session') is True
login.LoginFlow.login(context, username=username, password=password, resume_session=resume_session,
sso_master_password=kwargs.get('sso_password') is True)

auth = login.LoginFlow.login(
context.keeper_config, username=username, password=password, server=context.keeper_config.server,
resume_session=resume_session, sso_master_password=kwargs.get('sso_password') is True)
if auth is None:
raise base.CommandError("Login failed")
context.set_auth(auth)
# TODO check enforcements


Expand Down Expand Up @@ -78,11 +81,11 @@ def execute(self, context: params.KeeperParams, **kwargs):

if action == 'rename' or action == 'ren':
value = ops[1]
auth_utils.rename_device(context.auth, value)
keeper_auth.rename_device(context.auth, value)
logger.info(f'Successfully renamed device to {value}')

elif action == 'register':
is_device_registered = auth_utils.register_data_key_for_device(context.auth)
is_device_registered = keeper_auth.register_data_key_for_device(context.auth)
if is_device_registered:
logger.info('Successfully registered device')
else:
Expand All @@ -96,12 +99,12 @@ def execute(self, context: params.KeeperParams, **kwargs):
value = ops[1]

value_extracted = '1' if parse_utils.as_boolean(value) else '0'
auth_utils.set_user_setting(context.auth, 'persistent_login', value_extracted)
keeper_auth.set_user_setting(context.auth, 'persistent_login', value_extracted)
msg = 'ENABLED' if value_extracted == '1' else 'DISABLED'
logger.info(f'Successfully {msg} Persistent Login on this account')

if value_extracted == '1':
auth_utils.register_data_key_for_device(context.auth)
keeper_auth.register_data_key_for_device(context.auth)
_, this_device = ThisDeviceCommand.get_account_summary_and_this_device(context)

if this_device and not this_device.encryptedDataKeyPresent:
Expand All @@ -115,14 +118,14 @@ def execute(self, context: params.KeeperParams, **kwargs):
msg = 'ENABLED' if value_extracted == '1' else 'DISABLED'
# invert ip_auto_approve value before passing it to ip_disable_auto_approve
value_extracted = '0' if value_extracted == '1' else '1' if value_extracted == '0' else value_extracted
auth_utils.set_user_setting(context.auth, 'ip_disable_auto_approve', value_extracted)
keeper_auth.set_user_setting(context.auth, 'ip_disable_auto_approve', value_extracted)
logger.info(f'Successfully {msg} `ip_auto_approve`')

elif action == 'no-yubikey-pin':
value = ops[1]
value_extracted = '1' if parse_utils.as_boolean(value) else '0'
msg = 'ENABLED' if value_extracted == '0' else 'DISABLED'
auth_utils.set_user_setting(context.auth, 'security_keys_no_user_verify', value_extracted)
keeper_auth.set_user_setting(context.auth, 'security_keys_no_user_verify', value_extracted)
logger.info(f'Successfully {msg} Security Key PIN verification')

elif action == 'timeout' or action == 'to':
Expand All @@ -131,7 +134,7 @@ def execute(self, context: params.KeeperParams, **kwargs):
timeout_in_minutes = delta.seconds // 60
if timeout_in_minutes < 3:
timeout_in_minutes = 0
auth_utils.set_user_setting(context.auth, 'logout_timer', str(timeout_in_minutes))
keeper_auth.set_user_setting(context.auth, 'logout_timer', str(timeout_in_minutes))
display_value = 'default value' if delta == datetime.timedelta(0) else \
timeout_utils.format_timeout(delta)
logger.info('Successfully set "logout_timer" to %s.', display_value)
Expand All @@ -154,7 +157,7 @@ def is_persistent_login_disabled(context: params.KeeperParams) -> bool:
def get_account_summary_and_this_device(context: params.KeeperParams) \
-> Tuple[AccountSummary_pb2.AccountSummaryElements, AccountSummary_pb2.DeviceInfo]:
assert context.auth is not None
acct_summary = auth_utils.load_account_summary(context.auth)
acct_summary = keeper_auth.load_account_summary(context.auth)
devices = acct_summary.devices
current_device_token = context.auth.auth_context.device_token
this_device = next((x for x in devices if x.encryptedDeviceToken == current_device_token), None)
Expand Down
6 changes: 3 additions & 3 deletions keepercli-package/src/keepercli/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ def description(self):

def execute_args(self, context: KeeperParams, args: str, **kwargs):
value = self.validate(args)
if hasattr(context, self._attr_name):
if hasattr(context.keeper_config, self._attr_name):
if args:
setattr(context, self._attr_name, value)
setattr(context.keeper_config, self._attr_name, value)
else:
return getattr(context, self._attr_name)
return getattr(context.keeper_config, self._attr_name)

def validate(self, value: str) -> Any:
return value
Expand Down
2 changes: 1 addition & 1 deletion keepercli-package/src/keepercli/commands/cli_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class DebugCommand(base.ICliCommand):
def execute_args(self, context: KeeperParams, args: str, **kwargs):
logger = logging.getLogger()
is_debug = logger.getEffectiveLevel() <= logging.DEBUG
logger.setLevel((logging.WARNING if context.batch_mode else logging.INFO) if is_debug else logging.DEBUG)
logger.setLevel((logging.WARNING if context.keeper_config.batch_mode else logging.INFO) if is_debug else logging.DEBUG)
is_debug = logger.getEffectiveLevel() <= logging.DEBUG
prompt_utils.output_text('Debug ' + ('ON' if is_debug else 'OFF'))

Expand Down
Loading