Skip to content

Commit

Permalink
Add tests for SecureCRT groups parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxbey committed Sep 6, 2017
1 parent 44522e2 commit 81ee2f4
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 9 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ python:
- "2.7"
- "3.4"
- "3.5"
- "3.5-dev" # 3.5 development branch
install:
- git clone https://github.com/sstephenson/bats.git && cd bats && ./install.sh .. && cd -
- sudo apt-get install pandoc
Expand Down
7 changes: 5 additions & 2 deletions termius/porting/providers/securecrt/parser.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
"""Module with SecureCRT parser."""
import collections
from os.path import expanduser


class SecureCRTConfigParser(object):
"""SecureCRT xml parser."""

meta_sessions = ['Default']

def __init__(self, xml):
"""Construct parser instance."""
self.xml = xml
self.tree = {}

Expand All @@ -23,6 +24,7 @@ def parse_hosts(self):
return self.tree

def parse_sessions(self, sessions, parent_node):
"""Parse SecureCRT sessions."""
for session in sessions:
if session.get('name') not in self.meta_sessions:
if not self.is_session_group(session):
Expand All @@ -31,13 +33,14 @@ def parse_sessions(self, sessions, parent_node):
continue
parent_node[host['label']] = host
else:
parent_node[session.get('name')] = {'group': True}
parent_node[session.get('name')] = {'__group': True}
self.parse_sessions(
session.getchildren(),
parent_node[session.get('name')]
)

def is_session_group(self, session):
"""Check node element type"""
return self.get_element_by_name(
session.getchildren(), 'Hostname'
) is None
Expand Down
16 changes: 10 additions & 6 deletions termius/porting/providers/securecrt/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SecureCRTPortingProvider(BasePortingProvider):
logger = logging.getLogger(__name__)

def __init__(self, source, *args, **kwargs):
"""Contruct new service to sync ssh config."""
"""Construct provider instance."""
super(SecureCRTPortingProvider, self).__init__(*args, **kwargs)

xml_root = ElementTree.parse(source).getroot()
Expand All @@ -27,7 +27,7 @@ def export_hosts(self):
pass

def provider_hosts(self):
"""Retrieve host instances from ssh config."""
"""Retrieve SecureCRT sessions from xml config."""
result_hosts = []
tree = self.parser.parse_hosts()

Expand All @@ -54,14 +54,16 @@ def provider_hosts(self):

self.create_entries_from_tree(tree, result_hosts, root_group)
self.logger.info('Parsed hosts %i' % len(result_hosts))
self.logger.info('Importing...')
return result_hosts

def create_entries_from_tree(self, tree, result_hosts, parent_group=None):
for label, node in tree.iteritems():
"""Create instances from groups tree."""
for label, node in tree.items():
if not isinstance(node, dict):
continue

if not node.get('group', None):
if not node.get('__group', None):
result_hosts.append(
self.create_host(node, parent_group)
)
Expand All @@ -70,6 +72,7 @@ def create_entries_from_tree(self, tree, result_hosts, parent_group=None):
self.create_entries_from_tree(node, result_hosts, group)

def create_host(self, raw_host, group):
"""Create instances from groups tree."""
host = Host(
label=raw_host['label'],
address=raw_host['hostname'],
Expand All @@ -91,10 +94,11 @@ def create_host(self, raw_host, group):
return host

def create_key(self, identity_paths):
with open(identity_paths[0], 'r') as private_key_file:
"""Create ssh key instance."""
with open(identity_paths[0], 'rb') as private_key_file:
private_key = private_key_file.read()

with open(identity_paths[1], 'r') as public_key_file:
with open(identity_paths[1], 'rb') as public_key_file:
public_key = public_key_file.read()

return SshKey(
Expand Down
161 changes: 161 additions & 0 deletions tests/unit/porting/providers/securecrt_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
from io import BytesIO
from unittest import TestCase

from mock import patch, mock_open, call

from termius.core.models.terminal import Host, Group, SshConfig, Identity, \
SshKey
from termius.porting.providers.securecrt.provider import\
SecureCRTPortingProvider


class SecureCRTProviderTest(TestCase):
def test_tree_parsing(self):
securecrt_config = """<?xml version="1.0" encoding="UTF-8"?>
<VanDyke version="3.0">
<key name="Sessions">
<key name="host0">
<dword name="[SSH2] Port">0</dword>
<string name="Hostname">addr0</string>
<string name="Username">user0</string>
</key>
<key name="hosts">
<key name="folder0">
<key name="host1">
<dword name="[SSH2] Port">1</dword>
<string name="Hostname">addr1</string>
<string name="Username">user1</string>
</key>
</key>
<key name="folder1">
<key name="host2">
<dword name="[SSH2] Port">2</dword>
<string name="Hostname">addr2</string>
<string name="Username">user2</string>
</key>
<key name="serial">
<string name="Username">serial_user</string>
</key>
</key>
</key>
</key>
</VanDyke>
"""
xml_path = '/some/path/securecrt.xml'
root_group = Group(label='SecureCRT')
hosts_folder = Group(
label='hosts', parent_group=root_group
)
expected = [
Host(
label='host2',
address='addr2',
group=Group(label='folder1', parent_group=hosts_folder),
ssh_config=SshConfig(
port='2',
identity=Identity(
label='user2', username='user2', is_visible=False
)
)
),
Host(
label='host1',
address='addr1',
group=Group(label='folder0', parent_group=hosts_folder),
ssh_config=SshConfig(
port='1',
identity=Identity(
label='user1', username='user1', is_visible=False
)
)
),
Host(
label='host0', address='addr0', group=root_group,
ssh_config=SshConfig(
port='0',
identity=Identity(
label='user0', username='user0', is_visible=False
)
)
)
]

with patch('xml.etree.ElementTree.open',
mock_open(read_data=securecrt_config)) as mocked_xml:
service = SecureCRTPortingProvider(xml_path, None, '')
hosts = service.provider_hosts()
mocked_xml.assert_called_once_with(xml_path, 'rb')
self.assertEquals(
self.sort_hosts(hosts),
self.sort_hosts(expected)
)

def test_ssh2_identity_parsing(self):
public_key = b'public'
private_key = b'private'
xml_path = '/some/path/securecrt.xml'

root_group = Group(
label='SecureCRT',
ssh_config=SshConfig(
identity=Identity(
label='SecureCRT',
is_visible=False,
ssh_key=SshKey(
label='SecureCRT',
private_key=private_key,
public_key=public_key
)
)
)
)
securecrt_config = """<?xml version="1.0" encoding="UTF-8"?>
<VanDyke version="3.0">
<key name="Sessions">
<key name="host0">
<dword name="[SSH2] Port">0</dword>
<string name="Hostname">addr0</string>
<string name="Username">user0</string>
</key>
</key>
<key name="SSH2">
<string name="Identity Filename V2">/Users/termius/folder/key.pub::rawkey</string>
</key>
</VanDyke>
"""
expected = [
Host(
label='host0', group=root_group, address='addr0',
ssh_config=SshConfig(port='0',
identity=Identity(
label='user0',
is_visible=False,
username='user0'
)
)
)
]
expected_calls = [
call('/Users/termius/folder/key', 'rb'),
call('/Users/termius/folder/key.pub', 'rb')
]

with patch('xml.etree.ElementTree.open',
mock_open(read_data=securecrt_config)) as mocked_xml:

with patch('termius.porting.providers.securecrt.provider.open',
mock_open(read_data='')) as mocked_open:
service = SecureCRTPortingProvider(xml_path, None, '')
mocked_open.side_effect = [
BytesIO(private_key), BytesIO(public_key)
]
hosts = service.provider_hosts()
self.assertEquals(
self.sort_hosts(hosts),
self.sort_hosts(expected)
)
mocked_xml.assert_called_once_with(xml_path, 'rb')
mocked_open.assert_has_calls(expected_calls)

def sort_hosts(self, hosts):
return sorted(hosts, key=lambda host: host['address'])

0 comments on commit 81ee2f4

Please sign in to comment.