diff --git a/serverauditor_sshconfig/handlers/__init__.py b/serverauditor_sshconfig/handlers/__init__.py index 6876e9d..7413e52 100644 --- a/serverauditor_sshconfig/handlers/__init__.py +++ b/serverauditor_sshconfig/handlers/__init__.py @@ -5,6 +5,7 @@ from .snippet import SnippetCommand, SnippetsCommand # noqa from .pf_rule import PFRuleCommand, PFRulesCommand # noqa from .ssh_identity import SshIdentityCommand, SshIdentitiesCommand # noqa +from .ssh_key import SshKeyCommand, SshKeysCommand # noqa from .tag import TagsCommand # noqa from .info import InfoCommand # noqa from .connect import ConnectCommand # noqa diff --git a/serverauditor_sshconfig/handlers/connect.py b/serverauditor_sshconfig/handlers/connect.py index 453ba70..9494be4 100644 --- a/serverauditor_sshconfig/handlers/connect.py +++ b/serverauditor_sshconfig/handlers/connect.py @@ -11,6 +11,7 @@ class ConnectCommand(SshCommandFormatterMixin, SshConfigMergerMixin, GetRelationMixin, AbstractCommand): """Connect to specific host.""" + get_strategy = RelatedGetStrategy def get_parser(self, prog_name): diff --git a/serverauditor_sshconfig/handlers/group.py b/serverauditor_sshconfig/handlers/group.py index 1c84efb..57c24b4 100644 --- a/serverauditor_sshconfig/handlers/group.py +++ b/serverauditor_sshconfig/handlers/group.py @@ -23,10 +23,6 @@ def get_parser(self, prog_name): Use it to add extra options to argument parser. """ parser = super(GroupCommand, self).get_parser(prog_name) - parser.add_argument( - '--generate-key', action='store_true', - help='Create and assign automatically a identity file for group.' - ) parser.add_argument( '--ssh', help='Options in ssh_config format.' ) diff --git a/serverauditor_sshconfig/handlers/host.py b/serverauditor_sshconfig/handlers/host.py index de992f9..231b8d7 100644 --- a/serverauditor_sshconfig/handlers/host.py +++ b/serverauditor_sshconfig/handlers/host.py @@ -25,10 +25,6 @@ def get_parser(self, prog_name): Use it to add extra options to argument parser. """ parser = super(HostCommand, self).get_parser(prog_name) - parser.add_argument( - '--generate-key', action='store_true', - help='Create and assign automatically a identity file for host.' - ) parser.add_argument( '--ssh', metavar='SSH_CONFIG_OPTIONS', help='Options in ssh_config format.' diff --git a/serverauditor_sshconfig/handlers/info.py b/serverauditor_sshconfig/handlers/info.py index 6dc6abf..45cb169 100644 --- a/serverauditor_sshconfig/handlers/info.py +++ b/serverauditor_sshconfig/handlers/info.py @@ -53,7 +53,7 @@ def take_action(self, parsed_args): instance = self.get_relation( parsed_args.entry_type, parsed_args.id_or_name ) - ssh_config = self.get_merged_ssh_confi(instance) + ssh_config = self.get_merged_ssh_config(instance) return self.prepare_fields(ssh_config, instance) diff --git a/serverauditor_sshconfig/handlers/ssh_config.py b/serverauditor_sshconfig/handlers/ssh_config.py index a4b8938..768fd16 100644 --- a/serverauditor_sshconfig/handlers/ssh_config.py +++ b/serverauditor_sshconfig/handlers/ssh_config.py @@ -67,9 +67,6 @@ def serialize_args(self, args, instance): self.invalid_parameter_set(args) - if args.generate_key: - raise NotImplementedError('Not implemented') - if args.ssh_identity: ssh_identity = self.command.get_relation( SshIdentity, args.ssh_identity diff --git a/serverauditor_sshconfig/handlers/ssh_identity.py b/serverauditor_sshconfig/handlers/ssh_identity.py index 0b8c38c..761d73e 100644 --- a/serverauditor_sshconfig/handlers/ssh_identity.py +++ b/serverauditor_sshconfig/handlers/ssh_identity.py @@ -3,9 +3,11 @@ from operator import attrgetter from ..core.commands import DetailCommand, ListCommand from ..core.models.terminal import SshIdentity, SshKey +from ..core.exceptions import InvalidArgumentException +from .ssh_key import SshKeyGeneratorMixin -class SshIdentityCommand(DetailCommand): +class SshIdentityCommand(SshKeyGeneratorMixin, DetailCommand): """Operate with ssh identity object.""" allowed_operations = DetailCommand.all_operations @@ -17,10 +19,6 @@ def get_parser(self, prog_name): Use it to add extra options to argument parser. """ parser = super(SshIdentityCommand, self).get_parser(prog_name) - parser.add_argument( - '--generate-key', action='store_true', - help='Create and assign automatically a identity file for host.' - ) parser.add_argument( '-u', '--username', metavar='USERNAME', help="Username of host's user." @@ -54,21 +52,30 @@ def serialize_args(self, args, instance=None): identity = instance else: identity = SshIdentity() + ssh_key = None - if args.generate_key: - raise NotImplementedError('Not implemented') + self.check_incompatible_args(args) if args.identity_file: - raise NotImplementedError('Not implemented') + ssh_key = self.generate_ssh_key_instance(args) if args.ssh_key: - identity.ssh_key = self.get_relation(SshKey, args.ssh_key) + ssh_key = self.get_relation(SshKey, args.ssh_key) identity.username = args.username identity.password = args.password identity.is_visible = True + if ssh_key: + identity.ssh_key = ssh_key return identity + def check_incompatible_args(self, args): + """Raise an error when passed incompatible args.""" + if args.identity_file and args.ssh_key: + raise InvalidArgumentException( + 'You can not use ssh key and identity file together!' + ) + class SshIdentitiesCommand(ListCommand): """Manage ssh identity objects.""" diff --git a/serverauditor_sshconfig/handlers/ssh_key.py b/serverauditor_sshconfig/handlers/ssh_key.py new file mode 100644 index 0000000..72c981b --- /dev/null +++ b/serverauditor_sshconfig/handlers/ssh_key.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +"""Module with ssh key commands.""" +import os.path +from ..core.exceptions import ArgumentRequiredException +from ..core.commands import DetailCommand, ListCommand +from ..core.models.terminal import SshKey + + +# pylint: disable=too-few-public-methods +class SshKeyGeneratorMixin(object): + """Mixin for create new ssh key from file.""" + + # pylint: disable=no-self-use + def generate_ssh_key_instance(self, path): + """Generate ssh key from file.""" + with open(path, 'r') as _file: + content = _file.read() + label = os.path.basename(path) + return SshKey(private_key=content, label=label) + + +class SshKeyCommand(SshKeyGeneratorMixin, DetailCommand): + """Operate with Host object.""" + + allowed_operations = DetailCommand.all_operations + model_class = SshKey + + def get_parser(self, prog_name): + """Create command line argument parser. + + Use it to add extra options to argument parser. + """ + parser = super(SshKeyCommand, self).get_parser(prog_name) + parser.add_argument( + '-i', '--identity-file', + metavar='PRIVATE_KEY', help='Private key.' + ) + return parser + + def create(self, parsed_args): + """Handle create new instance command.""" + if not parsed_args.identity_file: + raise ArgumentRequiredException('Identity file is required.') + + self.create_instance(parsed_args) + + # pylint: disable=no-self-use + def serialize_args(self, args, instance=None): + """Convert args to instance.""" + if instance: + ssh_key = instance + if args.identity_file: + with open(args.identity_file, 'r') as _file: + ssh_key.private_key = _file.read() + else: + ssh_key = self.generate_ssh_key_instance(args.identity_file) + + if args.label: + ssh_key.label = args.label + return ssh_key + + +class SshKeysCommand(ListCommand): + """Manage ssh key objects.""" + + model_class = SshKey + + # pylint: disable=unused-argument + def take_action(self, parsed_args): + """Process CLI call.""" + instances = self.storage.get_all(self.model_class) + return self.prepare_result(instances) diff --git a/setup.py b/setup.py index 0756c14..3b2bf4e 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,8 @@ 'hosts = serverauditor_sshconfig.handlers:HostsCommand', 'identity = serverauditor_sshconfig.handlers:SshIdentityCommand', 'identities = serverauditor_sshconfig.handlers:SshIdentitiesCommand', + 'key = serverauditor_sshconfig.handlers:SshKeyCommand', + 'keys = serverauditor_sshconfig.handlers:SshKeysCommand', 'group = serverauditor_sshconfig.handlers:GroupCommand', 'groups = serverauditor_sshconfig.handlers:GroupsCommand', 'pfrule = serverauditor_sshconfig.handlers:PFRuleCommand', diff --git a/tests/integration/key.bats b/tests/integration/key.bats new file mode 100644 index 0000000..39cfe9b --- /dev/null +++ b/tests/integration/key.bats @@ -0,0 +1,66 @@ +#!/usr/bin/env bats + +setup() { + rm ~/.serverauditor.storage || true + touch key +} + +teardown() { + rm key +} + +@test "key help by arg" { + run serverauditor key --help + [ "$status" -eq 0 ] +} + +@test "key help command" { + run serverauditor help key + [ "$status" -eq 0 ] +} + +@test "Add general key" { + run serverauditor key -L test -i key + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} + +@test "Add many keys" { + run serverauditor key -L test_1 -i key + run serverauditor key -L test_2 -i key + run serverauditor key -L test_3 -i key + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} + +@test "Update key" { + key=$(serverauditor key -L test -i key) + run serverauditor key -i key $key + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} + +@test "Update many keys" { + key1=$(serverauditor key -L test_1 -i key) + key2=$(serverauditor key -L test_2 -i key) + key3=$(serverauditor key -L test_3 -i key) + run serverauditor key -i key $key1 $key2 $key3 + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} + +@test "Delete key" { + key=$(serverauditor key -L test_1 -i key) + run serverauditor key --delete $key + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} + +@test "Delete many keys" { + key1=$(serverauditor key -L test_1 -i key) + key2=$(serverauditor key -L test_2 -i key) + key3=$(serverauditor key -L test_3 -i key) + run serverauditor key --delete $key1 $key2 $key3 + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} diff --git a/tests/integration/keys.bats b/tests/integration/keys.bats new file mode 100644 index 0000000..e539071 --- /dev/null +++ b/tests/integration/keys.bats @@ -0,0 +1,27 @@ +#!/usr/bin/env bats + +setup() { + rm ~/.serverauditor.storage || true + touch key +} + +teardown() { + rm key +} + +@test "keys help by arg" { + run serverauditor keys --help + [ "$status" -eq 0 ] +} + +@test "keys help command" { + run serverauditor help keys + [ "$status" -eq 0 ] +} + +@test "List snippets in table format" { + serverauditor key -L test -i key + run serverauditor keys + [ "$status" -eq 0 ] + ! [ -z $(cat ~/.serverauditor.storage) ] +} diff --git a/tests/integration/snippet.bats b/tests/integration/snippet.bats index 3c6b7d5..dfbcb19 100644 --- a/tests/integration/snippet.bats +++ b/tests/integration/snippet.bats @@ -5,12 +5,12 @@ setup() { } @test "Snippet help by arg" { - run serverauditor host --help + run serverauditor snippet --help [ "$status" -eq 0 ] } @test "Snippet help command" { - run serverauditor help host + run serverauditor help snippet [ "$status" -eq 0 ] } diff --git a/tests/integration/snippets.bats b/tests/integration/snippets.bats index 67ba516..64a0061 100644 --- a/tests/integration/snippets.bats +++ b/tests/integration/snippets.bats @@ -4,13 +4,13 @@ setup() { rm ~/.serverauditor.storage || true } -@test "hosts help by arg" { - run serverauditor hosts --help +@test "snippets help by arg" { + run serverauditor snippets --help [ "$status" -eq 0 ] } -@test "hosts help command" { - run serverauditor help hosts +@test "snippets help command" { + run serverauditor help snippets [ "$status" -eq 0 ] }