Passed
Pull Request — master (#140)
by
unknown
01:47
created

gvmtools.parser.CliParser.add_protocol_argument()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2018 Greenbone Networks GmbH
3
#
4
# SPDX-License-Identifier: GPL-3.0-or-later
5
#
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
"""Command Line Interface Parser
20
"""
21
22
import argparse
23
import configparser
24
import logging
25
import os
26
27
from gvm import get_version as get_gvm_version
28
from gvm.connections import (DEFAULT_UNIX_SOCKET_PATH,
29
                             DEFAULT_TIMEOUT,
30
                             DEFAULT_GVM_PORT,
31
                             SSHConnection,
32
                             TLSConnection,
33
                             UnixSocketConnection)
34
35
from gvmtools import get_version
36
37
logger = logging.getLogger(__name__)
38
39
__version__ = get_version()
40
__api_version__ = get_gvm_version()
41
42
DEFAULT_CONFIG_PATH = '~/.config/gvm-tools.conf'
43
44
PROTOCOL_OSP = 'OSP'
45
PROTOCOL_GMP = 'GMP'
46
DEFAULT_PROTOCOL = PROTOCOL_GMP
47
48
49
class CliParser:
50
51
    def __init__(self, description, logfilename):
52
        root_parser = argparse.ArgumentParser(
53
            description=description,
54
            formatter_class=argparse.RawTextHelpFormatter,
55
            add_help=False)
56
57
        root_parser.add_argument(
58
            '-c', '--config', nargs='?', default=DEFAULT_CONFIG_PATH,
59
            help='Configuration file path (default: %(default)s)')
60
        root_parser.add_argument(
61
            '--log', nargs='?', dest='loglevel', const='INFO',
62
            choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
63
            help='Activate logging (default level: %(default)s)')
64
65
        args_before, _ = root_parser.parse_known_args()
66
67
        if args_before.loglevel is not None:
68
            level = logging.getLevelName(args_before.loglevel)
69
            logging.basicConfig(filename=logfilename, level=level)
70
71
        root_parser.add_argument(
72
            '--timeout', required=False, default=DEFAULT_TIMEOUT, type=int,
73
            help='Response timeout in seconds, or -1 to wait '
74
                 'indefinitely (default: %(default)s)')
75
        root_parser.add_argument(
76
            '--gmp-username',
77
            help='Username for GMP service (default: %(default)r)')
78
        root_parser.add_argument(
79
            '--gmp-password',
80
            help='Password for GMP service (default: %(default)r)')
81
        root_parser.add_argument(
82
            '-V', '--version', action='version',
83
            version='%(prog)s {version} (API version {apiversion})'.format(
84
                version=__version__, apiversion=__api_version__),
85
            help='Show version information and exit')
86
87
        parser = argparse.ArgumentParser(parents=[root_parser])
88
89
        subparsers = parser.add_subparsers(
90
            metavar='CONNECTION_TYPE',
91
            title='connections',
92
            description='valid connection types',
93
            help="Connection type to use",
94
        )
95
        subparsers.required = True
96
        subparsers.dest = 'connection_type'
97
98
        self._config = self._load_config(args_before.config)
99
100
        self._parser = parser
101
        self._root_parser = root_parser
102
        self._subparsers = subparsers
103
104
    def parse_args(self):
105
        self._add_subparsers()
106
107
        self._parser.set_defaults(
108
            gmp_username=self._config.get('gmp', 'username', fallback=''),
109
            gmp_password=self._config.get('gmp', 'password', fallback=''),
110
            **self._config.defaults()
111
        )
112
113
        args = self._parser.parse_args()
114
115
        # If timeout value is -1, then the socket should have no timeout
116
        if args.timeout == -1:
117
            args.timeout = None
118
119
120
        logging.debug('Parsed arguments %r', args)
121
122
        return args
123
124
    def add_argument(self, *args, **kwargs):
125
        self._parser.add_argument(*args, **kwargs)
126
        self._root_parser.add_argument(*args, **kwargs)
127
128
    def _load_config(self, configfile):
129
        config = configparser.ConfigParser(default_section='main')
130
131
        try:
132
            path = os.path.expanduser(configfile)
133
            config.read(path)
134
        except Exception as e: # pylint: disable=broad-except
135
            raise RuntimeError(
136
                'Error while parsing config file {config}. Error was '
137
                '{message}'.format(config=configfile, message=e))
138
139
        logger.debug('Loaded config %s', configfile)
140
        return config
141
142
    def _add_subparsers(self):
143
        parser_ssh = self._subparsers.add_parser(
144
            'ssh', help='Use SSH to connect to service',
145
            parents=[self._root_parser])
146
147
        parser_ssh.add_argument('--hostname', required=True,
148
                                help='Hostname or IP address')
149
        parser_ssh.add_argument('--port', required=False,
150
                                help='SSH port (default: %(default)s)')
151
        parser_ssh.add_argument('--ssh-username',
152
                                help='SSH username (default: %(default)r)')
153
        parser_ssh.add_argument('--ssh-password',
154
                                help='SSH password (default: %(default)r)')
155
156
        parser_ssh.set_defaults(
157
            port=int(self._config.get('ssh', 'port', fallback=22)),
158
            ssh_username=self._config.get('ssh', 'username', fallback='gmp'),
159
            ssh_password=self._config.get('ssh', 'password', fallback='gmp'),
160
        )
161
162
        parser_tls = self._subparsers.add_parser(
163
            'tls', help='Use TLS secured connection to connect to service',
164
            parents=[self._root_parser])
165
        parser_tls.add_argument('--hostname', required=True,
166
                                help='Hostname or IP address')
167
        parser_tls.add_argument('--port', required=False,
168
                                help='GMP/OSP port (default: %(default)s)')
169
        parser_tls.add_argument(
170
            '--certfile', required=False,
171
            help='Path to the certificate file for client authentication. '
172
                 '(default: %(default)s)')
173
        parser_tls.add_argument(
174
            '--keyfile', required=False,
175
            help='Path to key file for client authentication. '
176
                 '(default: %(default)s)')
177
        parser_tls.add_argument(
178
            '--cafile', required=False,
179
            help='Path to CA certificate for server authentication. '
180
                 '(default: %(default)s)')
181
        parser_tls.add_argument(
182
            '--no-credentials', required=False, default=False,
183
            help='Use only certificates for authentication')
184
        parser_tls.set_defaults(
185
            port=int(self._config.get(
186
                'tls', 'port', fallback=DEFAULT_GVM_PORT)),
187
            certfile=self._config.get('tls', 'certfile', fallback=None),
188
            keyfile=self._config.get('tls', 'keyfile', fallback=None),
189
            cafile=self._config.get('tls', 'cafile', fallback=None),
190
        )
191
192
        parser_socket = self._subparsers.add_parser(
193
            'socket', help='Use UNIX Domain socket to connect to service',
194
            parents=[self._root_parser])
195
196
        socketpath_group = parser_socket.add_mutually_exclusive_group()
197
        socketpath_group.add_argument(
198
            '--sockpath', nargs='?', default=None,
199
            help='Deprecated, use --socketpath instead')
200
        socketpath_group.add_argument(
201
            '--socketpath', nargs='?',
202
            help='Path to UNIX Domain socket (default: %(default)s)')
203
204
        parser_socket.set_defaults(
205
            socketpath=self._config.get(
206
                'unixsocket', 'socketpath', fallback=DEFAULT_UNIX_SOCKET_PATH),
207
        )
208
209
    def add_protocol_argument(self):
210
        self.add_argument(
211
            '--protocol', required=False, default=DEFAULT_PROTOCOL,
212
            choices=[PROTOCOL_GMP, PROTOCOL_OSP],
213
            help='Service protocol to use (default: %(default)s)')
214
215
216
def create_parser(description, logfilename):
217
    return CliParser(description, logfilename)
218
219
220
def create_connection(connection_type, socketpath=None, timeout=None,
221
                      hostname=None, port=None, certfile=None, keyfile=None,
222
                      cafile=None, ssh_username=None, ssh_password=None,
223
                      **kwargs):
224
    if 'socket' in connection_type:
225
        return UnixSocketConnection(
226
            timeout=timeout,
227
            path=socketpath
228
        )
229
230
    if 'tls' in connection_type:
231
        return TLSConnection(
232
            timeout=timeout,
233
            hostname=hostname,
234
            port=port,
235
            certfile=certfile,
236
            keyfile=keyfile,
237
            cafile=cafile,
238
        )
239
240
    return SSHConnection(
241
        timeout=timeout,
242
        hostname=hostname,
243
        port=port,
244
        username=ssh_username,
245
        password=ssh_password
246
    )
247