diff --git a/termius/cloud/commands.py b/termius/cloud/commands.py index 63461be..4666c15 100644 --- a/termius/cloud/commands.py +++ b/termius/cloud/commands.py @@ -4,6 +4,8 @@ from base64 import b64decode import six from six.moves import configparser + +from ..core.exceptions import AuthyTokenIssue from ..core.api import API from ..core.commands import AbstractCommand from ..core.models.terminal import clean_order @@ -26,6 +28,11 @@ def process_sync(self, api_controller): """Do sync staff here.""" pass + # pylint: disable=no-self-use + def prompt_authy_token(self): + """Ask authy token prompt.""" + return six.moves.input('Authy token: ') + def take_action(self, parsed_args): """Process CLI call.""" encryption_salt = b64decode(self.config.get('User', 'salt')) @@ -45,7 +52,12 @@ def take_action(self, parsed_args): def validate_password(self, password): """Raise an error when password invalid.""" username = self.config.get('User', 'username') - API().login(username, password) + api = API() + try: + api.login(username, password) + except AuthyTokenIssue: + authy_token = self.prompt_authy_token() + api.login(username, password, authy_token=authy_token) class PushCommand(CloudSynchronizationCommand): diff --git a/termius/core/commands/base.py b/termius/core/commands/base.py index f766a5e..8503614 100644 --- a/termius/core/commands/base.py +++ b/termius/core/commands/base.py @@ -24,6 +24,8 @@ class AbstractCommand(PasswordPromptMixin, Command): get_strategy = GetStrategy delete_strategy = SoftDeleteStrategy + skip_fields = ['remote_instance'] + def __init__(self, app, app_args, cmd_name=None): """Construct new command.""" super(AbstractCommand, self).__init__(app, app_args, cmd_name) diff --git a/termius/core/commands/mixins.py b/termius/core/commands/mixins.py index 9d6d9df..e02d706 100644 --- a/termius/core/commands/mixins.py +++ b/termius/core/commands/mixins.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Module with different CLI commands mixins.""" import getpass +import os from operator import attrgetter from functools import partial from cached_property import cached_property @@ -77,7 +78,7 @@ def prepare_fields(self): def prepare_result(self, found_list): """Return tuple with data in format for Lister.""" - fields = self.prepare_fields + fields = sorted(list(set(self.prepare_fields) - set(self.skip_fields))) getter = DefaultAttrGetter(*fields) return fields, [getter(i) for i in found_list] @@ -229,7 +230,9 @@ def log_delete(self, entry): self._general_log(entry, 'Entry deleted.') def _general_log(self, entry, message): - self.app.stdout.write('{}\n'.format(entry.id)) + if os.getenv('TERMIUS_CLI_DEBUG'): + self.app.stdout.write('{}\n'.format(entry.id)) + self.log.info(message) diff --git a/termius/handlers/group.py b/termius/handlers/group.py index 7bcb6cf..1aa4f10 100644 --- a/termius/handlers/group.py +++ b/termius/handlers/group.py @@ -4,8 +4,9 @@ from operator import attrgetter from cached_property import cached_property from ..core.commands import DetailCommand, ListCommand -from ..core.commands.mixins import GroupStackGetterMixin, SshConfigPrepareMixin +from ..core.commands.mixins import GroupStackGetterMixin from ..core.models.terminal import Group +from ..core.commands.single import RequiredOptions from ..core.storage.strategies import RelatedGetStrategy from ..core.exceptions import InvalidArgumentException from .ssh_config import SshConfigArgs @@ -15,6 +16,7 @@ class GroupCommand(GroupStackGetterMixin, DetailCommand): """work with a group""" model_class = Group + required_options = RequiredOptions(create=('label',)) @cached_property def fields(self): @@ -63,7 +65,7 @@ def serialize_args(self, args, instance=None): return instance -class GroupsCommand(SshConfigPrepareMixin, ListCommand): +class GroupsCommand(ListCommand): """list all groups""" model_class = Group diff --git a/termius/handlers/host.py b/termius/handlers/host.py index 82af93c..6353e88 100644 --- a/termius/handlers/host.py +++ b/termius/handlers/host.py @@ -15,7 +15,7 @@ class HostCommand(DetailCommand): """work with a host""" model_class = Host - required_options = RequiredOptions(create=('address',)) + required_options = RequiredOptions(create=('address', 'label')) @cached_property def fields(self): diff --git a/termius/handlers/identity.py b/termius/handlers/identity.py index 01e3eea..d6f49db 100644 --- a/termius/handlers/identity.py +++ b/termius/handlers/identity.py @@ -5,6 +5,7 @@ from cached_property import cached_property from ..core.commands import DetailCommand, ListCommand from ..core.models.terminal import Identity, SshKey +from ..core.commands.single import RequiredOptions from ..core.exceptions import InvalidArgumentException, DoesNotExistException from .ssh_key import SshKeyGeneratorMixin @@ -13,6 +14,7 @@ class IdentityCommand(SshKeyGeneratorMixin, DetailCommand): """work with an identity""" model_class = Identity + required_options = RequiredOptions(create=('label',)) @cached_property def fields(self): @@ -34,7 +36,9 @@ def get_ssh_key_field(self, args): 'You can not use ssh key and identity file together!' ) if args.identity_file: - return self.generate_ssh_key_instance(args.identity_file) + return self.generate_ssh_key_instance( + args.identity_file, self.storage + ) if args.ssh_key: return self.get_safely_instance(SshKey, args.ssh_key) diff --git a/termius/handlers/snippet.py b/termius/handlers/snippet.py index 4301b2d..6c9a7b1 100644 --- a/termius/handlers/snippet.py +++ b/termius/handlers/snippet.py @@ -9,7 +9,7 @@ class SnippetCommand(DetailCommand): """work with snippet""" model_class = Snippet - required_options = RequiredOptions(create=('script',)) + required_options = RequiredOptions(create=('script', 'label')) def extend_parser(self, parser): """Add more arguments to parser.""" diff --git a/termius/handlers/ssh_config.py b/termius/handlers/ssh_config.py index 77d1e94..04aa657 100644 --- a/termius/handlers/ssh_config.py +++ b/termius/handlers/ssh_config.py @@ -31,7 +31,7 @@ def fields(self): def get_ssh_key_field(self, args): """Create ssh key instance from args.""" return args.identity_file and self.generate_ssh_key_instance( - args.identity_file + args.identity_file, self.command.storage ) # pylint: disable=no-self-use diff --git a/termius/handlers/ssh_key.py b/termius/handlers/ssh_key.py index 3efbb61..419ff86 100644 --- a/termius/handlers/ssh_key.py +++ b/termius/handlers/ssh_key.py @@ -14,19 +14,19 @@ class SshKeyGeneratorMixin(object): """Mixin for create new ssh key from file.""" # pylint: disable=no-self-use - def generate_ssh_key_instance(self, path): + def generate_ssh_key_instance(self, path, storage): """Generate ssh key from file.""" private_key_path = Path(path) instance = SshKey( private_key=private_key_path.read_text(), label=private_key_path.name ) - self.validate_ssh_key(instance) + self.validate_ssh_key(instance, storage) return instance - def validate_ssh_key(self, instance): + def validate_ssh_key(self, instance, storage): """Raise an error when any instances exist with same label.""" - with_same_label = self.storage.filter( + with_same_label = storage.filter( SshKey, **{'label': instance.label, 'id.ne': instance.id} ) if with_same_label: @@ -63,7 +63,7 @@ def get_private_key(self, args): def validate(self, instance): """Raise an error when any instances exist with same label.""" - self.validate_ssh_key(instance) + self.validate_ssh_key(instance, self.storage) class SshKeysCommand(ListCommand): diff --git a/termius/porting/providers/ssh/provider.py b/termius/porting/providers/ssh/provider.py index 780b2a6..5fe1969 100644 --- a/termius/porting/providers/ssh/provider.py +++ b/termius/porting/providers/ssh/provider.py @@ -35,7 +35,7 @@ def export_hosts(self): for host in hosts_in_storage: self.export_host( export_file, - host['label'] or host['address'], + host.get('label', host['address']), self.adapter.adapt_instance_to_ssh_config_host(host) ) diff --git a/tests/integration/group.bats b/tests/integration/group.bats index b40aee2..80ad916 100644 --- a/tests/integration/group.bats +++ b/tests/integration/group.bats @@ -34,7 +34,7 @@ setup() { @test "Add group to main group" { group=$(termius group -L 'test group' --port 2 --username 'use r name') - run termius group --port 22 --parent-group $group + run termius group --port 22 --parent-group $group -L subgroup [ "$status" -eq 0 ] [ $(get_models_set_length 'group_set') -eq 2 ] [ $(get_models_set_length 'sshconfig_set') -eq 2 ] diff --git a/tests/integration/host.bats b/tests/integration/host.bats index f3caa45..c21f155 100644 --- a/tests/integration/host.bats +++ b/tests/integration/host.bats @@ -33,7 +33,7 @@ setup() { } @test "Add host to group" { - group=$(termius group --port 2022) + group=$(termius group --port 2022 -L group) run termius host -L test --group $group --address localhost --debug [ "$status" -eq 0 ] [ $(get_models_set_length 'host_set') -eq 1 ] @@ -79,7 +79,7 @@ setup() { } @test "Update host add to group" { - group=$(termius group --port 2022) + group=$(termius group --port 2022 -L group) host=$(termius host --address localhost -L test) run termius host --group $group $host [ "$status" -eq 0 ] diff --git a/tests/integration/hosts.bats b/tests/integration/hosts.bats index 3737a0d..4a75e5e 100644 --- a/tests/integration/hosts.bats +++ b/tests/integration/hosts.bats @@ -34,7 +34,7 @@ setup() { } @test "List hosts in a group" { - group=$(termius group --port 2022) + group=$(termius group --port 2022 -L group) termius host -L test --address localhost --username root --password password host_id=$(termius host -L test --group $group --address localhost --username root --password password) @@ -47,7 +47,7 @@ setup() { } @test "List hosts in the root group" { - group=$(termius group --port 2022) + group=$(termius group --port 2022 -L group) host_id=$(termius host -L test --group $group --address localhost --username root --password password) run termius hosts -f csv -c id @@ -59,8 +59,8 @@ setup() { } @test "List hosts in child groups, too" { - group=$(termius group --port 2022) - child_group=$(termius group --parent-group $group --port 2022) + group=$(termius group --port 2022 -L group) + child_group=$(termius group --parent-group $group --port 2022 -L subgroup) termius host -L test --address localhost --username root --password password host_id=$(termius host -L test --group $child_group --address localhost --username root --password password) @@ -73,8 +73,8 @@ setup() { } @test "List hosts only in child groups" { - group=$(termius group --port 2022) - child_group=$(termius group --parent-group $group --port 2022) + group=$(termius group --port 2022 -L group) + child_group=$(termius group --parent-group $group --port 2022 -L subgroup) termius host -L test --address localhost --username root --password password termius host -L test --group $group --address localhost --username root --password password host_id=$(termius host -L test --group $child_group --address localhost --username root --password password) @@ -88,7 +88,7 @@ setup() { } @test "List hosts in a group filter by the tag" { - group=$(termius group --port 2022) + group=$(termius group --port 2022 -L group) host_id=$(termius host -L test --group $group --address localhost --username root --password password -t A) termius host -L test --address localhost --username root --password password -t A @@ -100,7 +100,7 @@ setup() { } @test "List hosts filter by a tag acrous all gropus" { - group=$(termius group --port 2022) + group=$(termius group --port 2022 -L group) host_id=$(termius host -L test --group $group --address localhost --username root --password password -t A) run termius hosts --tag A -f csv -c id diff --git a/tests/integration/info.bats b/tests/integration/info.bats index 463e398..b24df3a 100644 --- a/tests/integration/info.bats +++ b/tests/integration/info.bats @@ -81,7 +81,7 @@ setup() { @test "info host in 2 groups with visible identity" { termius settings --agent-forwarding yes - identity=$(termius identity --username user) + identity=$(termius identity --username user -L identity) grandgroup=$(termius group -L test --port 22 --identity $identity) group=$(termius group --parent-group $grandgroup -L test --port 2022 --username local) host=$(termius host --group $group --address localhost -L test --username root) @@ -93,7 +93,7 @@ setup() { @test "info host in 2 groups with visible identity without agent forwarding" { termius settings --agent-forwarding no - identity=$(termius identity --username user) + identity=$(termius identity --username user -L identity) grandgroup=$(termius group -L test --port 22 --identity $identity) group=$(termius group --parent-group $grandgroup -L test --port 2022 --username local) host=$(termius host --group $group --address localhost -L test --username root) diff --git a/tests/integration/snippet.bats b/tests/integration/snippet.bats index a50be99..43d21c4 100644 --- a/tests/integration/snippet.bats +++ b/tests/integration/snippet.bats @@ -38,6 +38,7 @@ setup() { run termius snippet --script 'cd /' $snippet [ "$status" -eq 0 ] [ $(get_models_set_length 'snippet_set') -eq 1 ] + [ "$(get_model_field 'snippet_set' $snippet 'label')" = '"test"' ] [ "$(get_model_field 'snippet_set' $snippet 'script')" = "\"cd /\"" ] }