Passed
Pull Request — master (#115)
by
unknown
04:16 queued 41s
created

gvm.protocols.gmpv8   F

Complexity

Total Complexity 81

Size/Duplication

Total Lines 551
Duplicated Lines 8.53 %

Importance

Changes 0
Metric Value
eloc 234
dl 47
loc 551
rs 2
c 0
b 0
f 0
wmc 81

5 Methods

Rating   Name   Duplication   Size   Complexity  
F Gmp.create_credential() 47 221 30
A Gmp.get_protocol_version() 0 8 1
F Gmp.modify_credential() 0 122 23
C Gmp.create_tag() 0 70 11
F Gmp.modify_tag() 0 84 16

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like gvm.protocols.gmpv8 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
# pylint: disable=arguments-differ
20
21
"""
22
Module for communication with gvmd in `Greenbone Management Protocol version 8`_
23
24
**GMP Version 8 has not been released yet**
25
26
.. _Greenbone Management Protocol version 8:
27
    https://docs.greenbone.net/API/GMP/gmp-8.0.html
28
"""
29
30
from gvm.errors import InvalidArgument, RequiredArgument
31
from gvm.utils import get_version_string
32
from gvm.xml import XmlCommand
33
34
from .gmpv7 import Gmp as Gmpv7, _to_bool
35
36
CREDENTIAL_TYPES = ("cc", "snmp", "up", "usk", "smime", "pgp")
37
38
PROTOCOL_VERSION = (8,)
39
40
41
class Gmp(Gmpv7):
42
    @staticmethod
43
    def get_protocol_version():
44
        """Allow to determine the Greenbone Management Protocol version.
45
46
            Returns:
47
                str: Implemented version of the Greenbone Management Protocol
48
        """
49
        return get_version_string(PROTOCOL_VERSION)
50
51
    def create_credential(
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (19/15).
Loading history...
52
        self,
53
        name,
54
        credential_type,
55
        *,
56
        comment=None,
57
        allow_insecure=None,
58
        certificate=None,
59
        key_phrase=None,
60
        private_key=None,
61
        login=None,
62
        password=None,
63
        auth_algorithm=None,
64
        community=None,
65
        privacy_algorithm=None,
66
        privacy_password=None,
67
        public_key=None
68
    ):
69
        """Create a new credential
70
71
        Create a new credential e.g. to be used in the method of an alert.
72
73
        Currently the following credential types are supported:
74
75
            - 'up'    - Username + Password
76
            - 'usk'   - Username + private SSH-Key
77
            - 'cc'    - Client Certificates
78
            - 'snmp'  - SNMPv1 or SNMPv2c protocol
79
            - 'smime' - S/MIME Certificate
80
            - 'pgp'   - OpenPGP Key
81
82
        Arguments:
83
            name (str): Name of the new credential
84
            credential_type (str): The credential type. One of 'cc', 'snmp',
85
                'up', 'usk', 'smime', 'pgp'
86
            comment (str, optional): Comment for the credential
87
            allow_insecure (boolean, optional): Whether to allow insecure use of
88
                the credential
89
            certificate (str, optional): Certificate for the credential.
90
                Required for cc and smime credential types.
91
            key_phrase (str, optional): Key passphrase for the private key.
92
                Used for the usk credential type.
93
            private_key (str, optional): Private key to use for login. Required
94
                for usk credential type. Also used for the cc credential type.
95
                The supported key types (dsa, rsa, ecdsa, ...) and formats (PEM,
96
                PKC#12, OpenSSL, ...) depend on your installed GnuTLS version.
97
            login (str, optional): Username for the credential. Required for
98
                up, usk and snmp credential type.
99
            password (str, optional): Password for the credential. Used for
100
                up and snmp credential types.
101
            community (str, optional): The SNMP community
102
            auth_algorithm (str, optional): The SNMP authentication algorithm.
103
                Either 'md5' or 'sha1'. Required for snmp credential type.
104
            privacy_algorithm (str, optional): The SNMP privacy algorithm,
105
                either aes or des.
106
            privacy_password (str, optional): The SNMP privacy password
107
            public_key: (str, optional): PGP public key in *armor* plain text
108
                format. Required for pgp credential type.
109
110
        Examples:
111
            Creating a Username + Password credential
112
113
            .. code-block:: python
114
115
                gmp.create_credential(
116
                    name='UP Credential',
117
                    credential_type='up',
118
                    login='foo',
119
                    password='bar',
120
                );
121
122
            Creating a Username + SSH Key credential
123
124
            .. code-block:: python
125
126
                with open('path/to/private-ssh-key') as f:
127
                    key = f.read()
128
129
                gmp.create_credential(
130
                    name='USK Credential',
131
                    credential_type='usk',
132
                    login='foo',
133
                    key_phrase='foobar',
134
                    private_key=key,
135
                )
136
137
            Creating a PGP credential
138
139
            .. note::
140
141
                A compatible public pgp key file can be exported with GnuPG via
142
                ::
143
144
                    $ gpg --armor --export [email protected] > alice.asc
145
146
            .. code-block:: python
147
148
                with open('path/to/pgp.key.asc') as f:
149
                    key = f.read()
150
151
                gmp.create_credential(
152
                    name='PGP Credential',
153
                    credential_type='pgp',
154
                    public_key=key,
155
                )
156
157
            Creating a S/MIME credential
158
159
            .. code-block:: python
160
161
                with open('path/to/smime-cert') as f:
162
                    cert = f.read()
163
164
                gmp.create_credential(
165
                    name='SMIME Credential',
166
                    credential_type='smime',
167
                    certificate=cert,
168
                )
169
170
        Returns:
171
            The response. See :py:meth:`send_command` for details.
172
        """
173
        if not name:
174
            raise RequiredArgument("create_credential requires name argument")
175
176
        if credential_type not in CREDENTIAL_TYPES:
177
            raise InvalidArgument(
178
                "create_credential requires type to be either cc, snmp, up,"
179
                "smime, pgp or usk"
180
            )
181
182
        cmd = XmlCommand("create_credential")
183
        cmd.add_element("name", name)
184
185
        cmd.add_element("type", credential_type)
186
187
        if comment:
188
            cmd.add_element("comment", comment)
189
190
        if allow_insecure is not None:
191
            cmd.add_element("allow_insecure", _to_bool(allow_insecure))
192
193
        if credential_type == "cc" or credential_type == "smime":
0 ignored issues
show
Unused Code introduced by
Consider merging these comparisons with "in" to "credential_type in ('cc', 'smime')"
Loading history...
194
            if not certificate:
195
                raise RequiredArgument(
196
                    "create_credential requires certificate argument for "
197
                    "credential_type {0}".format(credential_type)
198
                )
199
200
            cmd.add_element("certificate", certificate)
201
202 View Code Duplication
        if (
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
203
            credential_type == "up"
0 ignored issues
show
Unused Code introduced by
Consider merging these comparisons with "in" to "credential_type in ('up', 'usk', 'snmp')"
Loading history...
204
            or credential_type == "usk"
205
            or credential_type == "snmp"
206
        ):
207
            if not login:
208
                raise RequiredArgument(
209
                    "create_credential requires login argument for "
210
                    "credential_type {0}".format(credential_type)
211
                )
212
213
            cmd.add_element("login", login)
214
215
        if (credential_type == "up" or credential_type == "snmp") and password:
0 ignored issues
show
Unused Code introduced by
Consider merging these comparisons with "in" to "credential_type in ('up', 'snmp')"
Loading history...
216
            cmd.add_element("password", password)
217
218 View Code Duplication
        if credential_type == "usk":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
219
            if not private_key:
220
                raise RequiredArgument(
221
                    "create_credential requires certificate argument for "
222
                    "credential_type usk"
223
                )
224
225
            _xmlkey = cmd.add_element("key")
226
            _xmlkey.add_element("private", private_key)
227
228
            if key_phrase:
229
                _xmlkey.add_element("phrase", key_phrase)
230
231
        if credential_type == "cc" and private_key:
232
            _xmlkey = cmd.add_element("key")
233
            _xmlkey.add_element("private", private_key)
234
235 View Code Duplication
        if credential_type == "snmp":
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
236
            if auth_algorithm not in ("md5", "sha1"):
237
                raise InvalidArgument(
238
                    "create_credential requires auth_algorithm to be either "
239
                    "md5 or sha1"
240
                )
241
242
            cmd.add_element("auth_algorithm", auth_algorithm)
243
244
            if community:
245
                cmd.add_element("community", community)
246
247
            if privacy_algorithm is not None or privacy_password:
248
                _xmlprivacy = cmd.add_element("privacy")
249
250
                if privacy_algorithm is not None:
251
                    if privacy_algorithm not in ("aes", "des"):
252
                        raise InvalidArgument(
253
                            "create_credential requires algorithm to be either "
254
                            "aes or des"
255
                        )
256
257
                    _xmlprivacy.add_element("algorithm", privacy_algorithm)
258
259
                if privacy_password:
260
                    _xmlprivacy.add_element("password", privacy_password)
261
262
        if credential_type == "pgp":
263
            if not public_key:
264
                raise RequiredArgument(
265
                    "Creating a pgp credential requires a public_key argument"
266
                )
267
268
            _xmlkey = cmd.add_element("key")
269
            _xmlkey.add_element("public", public_key)
270
271
        return self._send_xml_command(cmd)
272
273
    def modify_credential(
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (20/15).
Loading history...
274
        self,
275
        credential_id,
276
        *,
277
        name=None,
278
        comment=None,
279
        allow_insecure=None,
280
        certificate=None,
281
        key_phrase=None,
282
        private_key=None,
283
        login=None,
284
        password=None,
285
        auth_algorithm=None,
286
        community=None,
287
        privacy_algorithm=None,
288
        privacy_password=None,
289
        credential_type=None,
290
        public_key=None
291
    ):
292
        """Modifies an existing credential.
293
294
        Arguments:
295
            credential_id (str): UUID of the credential
296
            name (str, optional): Name of the credential
297
            comment (str, optional): Comment for the credential
298
            allow_insecure (boolean, optional): Whether to allow insecure use of
299
                 the credential
300
            certificate (str, optional): Certificate for the credential
301
            key_phrase (str, optional): Key passphrase for the private key
302
            private_key (str, optional): Private key to use for login
303
            login (str, optional): Username for the credential
304
            password (str, optional): Password for the credential
305
            auth_algorithm (str, optional): The auth_algorithm,
306
                either md5 or sha1.
307
            community (str, optional): The SNMP community
308
            privacy_algorithm (str, optional): The SNMP privacy algorithm,
309
                either aes or des.
310
            privacy_password (str, optional): The SNMP privacy password
311
            credential_type (str, optional): The credential type. One of 'cc',
312
                'snmp', 'up', 'usk', 'smime', 'pgp'
313
            public_key: (str, optional): PGP public key in *armor* plain text
314
                format
315
316
        Returns:
317
            The response. See :py:meth:`send_command` for details.
318
        """
319
        if not credential_id:
320
            raise RequiredArgument(
321
                "modify_credential requires " "a credential_id attribute"
322
            )
323
324
        cmd = XmlCommand("modify_credential")
325
        cmd.set_attribute("credential_id", credential_id)
326
327
        if credential_type:
328
            if credential_type not in CREDENTIAL_TYPES:
329
                raise InvalidArgument(
330
                    "modify_credential requires type to be either cc, snmp, up "
331
                    "smime, pgp or usk"
332
                )
333
            cmd.add_element("type", credential_type)
334
335
        if comment:
336
            cmd.add_element("comment", comment)
337
338
        if name:
339
            cmd.add_element("name", name)
340
341
        if allow_insecure is not None:
342
            cmd.add_element("allow_insecure", _to_bool(allow_insecure))
343
344
        if certificate:
345
            cmd.add_element("certificate", certificate)
346
347
        if key_phrase or private_key:
348
            if not key_phrase or not private_key:
349
                raise RequiredArgument(
350
                    "modify_credential requires "
351
                    "a key_phrase and private_key arguments"
352
                )
353
            _xmlkey = cmd.add_element("key")
354
            _xmlkey.add_element("phrase", key_phrase)
355
            _xmlkey.add_element("private", private_key)
356
357
        if login:
358
            cmd.add_element("login", login)
359
360
        if password:
361
            cmd.add_element("password", password)
362
363
        if auth_algorithm:
364
            if auth_algorithm not in ("md5", "sha1"):
365
                raise InvalidArgument(
366
                    "modify_credential requires "
367
                    "auth_algorithm to be either "
368
                    "md5 or sha1"
369
                )
370
            cmd.add_element("auth_algorithm", auth_algorithm)
371
372
        if community:
373
            cmd.add_element("community", community)
374
375
        if privacy_algorithm is not None or privacy_password is not None:
376
            _xmlprivacy = cmd.add_element("privacy")
377
378
            if privacy_algorithm is not None:
379
                if privacy_algorithm not in ("aes", "des"):
380
                    raise InvalidArgument(
381
                        "modify_credential requires privacy_algorithm to be "
382
                        "either aes or des"
383
                    )
384
385
                _xmlprivacy.add_element("algorithm", privacy_algorithm)
386
387
            if privacy_password is not None:
388
                _xmlprivacy.add_element("password", privacy_password)
389
390
        if public_key:
391
            _xmlkey = cmd.add_element("key")
392
            _xmlkey.add_element("public", public_key)
393
394
        return self._send_xml_command(cmd)
395
396
    def create_tag(
397
        self,
398
        name,
399
        resource_type,
400
        *,
401
        resource_filter=None,
402
        resource_ids=None,
403
        value=None,
404
        comment=None,
405
        active=None
406
    ):
407
        """Create a tag.
408
409
        Arguments:
410
            name (str): Name of the tag. A full tag name consisting of
411
                namespace and predicate e.g. `foo:bar`.
412
            resource_type (str): Entity type the tag is to be attached
413
                to.
414
            resource_filter (str, optional) Filter term to select
415
                resources the tag is to be attached to. Either
416
                resource_filter or resource_ids must be provided.
417
            resource_ids (list, optional): IDs of the resources the
418
                tag is to be attached to. Either resource_filter or
419
                resource_ids must be provided.
420
            value (str, optional): Value associated with the tag.
421
            comment (str, optional): Comment for the tag.
422
            active (boolean, optional): Whether the tag should be
423
                active.
424
425
        Returns:
426
            The response. See :py:meth:`send_command` for details.
427
        """
428
        if not name:
429
            raise RequiredArgument("create_tag requires name argument")
430
431
        if not resource_filter and not resource_ids:
432
            raise RequiredArgument(
433
                "create_tag requires resource_filter or resource_ids argument"
434
            )
435
436
        if not resource_type:
437
            raise RequiredArgument("create_tag requires resource_type argument")
438
439
        cmd = XmlCommand('create_tag')
440
        cmd.add_element('name', name)
441
442
        _xmlresources = cmd.add_element("resources")
443
        if resource_filter is not None:
444
            _xmlresources.set_attribute("filter", resource_filter)
445
446
        for resource_id in resource_ids or []:
447
            _xmlresources.add_element(
448
                "resource", attrs={"id": str(resource_id)}
449
            )
450
451
        _xmlresources.add_element("type", resource_type)
452
453
        if comment:
454
            cmd.add_element("comment", comment)
455
456
        if value:
457
            cmd.add_element("value", value)
458
459
        if active is not None:
460
            if active:
461
                cmd.add_element("active", "1")
462
            else:
463
                cmd.add_element("active", "0")
464
465
        return self._send_xml_command(cmd)
466
467
    def modify_tag(
468
        self,
469
        tag_id,
470
        *,
471
        comment=None,
472
        name=None,
473
        value=None,
474
        active=None,
475
        resource_action=None,
476
        resource_type=None,
477
        resource_filter=None,
478
        resource_ids=None
479
    ):
480
        """Modifies an existing tag.
481
482
        Arguments:
483
            tag_id (str): UUID of the tag.
484
            comment (str, optional): Comment to add to the tag.
485
            name (str, optional): Name of the tag.
486
            value (str, optional): Value of the tag.
487
            active (boolean, optional): Whether the tag is active.
488
            resource_action (str, optional) Whether to add or remove
489
                resources instead of overwriting. One of '', 'add',
490
                'set' or 'remove'.
491
            resource_type (str, optional): Type of the resources to
492
                which to attach the tag. Required if resource_filter
493
                or resource_ids is set.
494
            resource_filter (str, optional) Filter term to select
495
                resources the tag is to be attached to. Required if
496
                resource_type is set unless resource_ids is set.
497
            resource_ids (list, optional): IDs of the resources to
498
                which to attach the tag. Required if resource_type is
499
                set unless resource_filter is set.
500
501
        Returns:
502
            The response. See :py:meth:`send_command` for details.
503
        """
504
        if not tag_id:
505
            raise RequiredArgument("modify_tag requires a tag_id element")
506
507
        cmd = XmlCommand("modify_tag")
508
        cmd.set_attribute("tag_id", str(tag_id))
509
510
        if comment:
511
            cmd.add_element("comment", comment)
512
513
        if name:
514
            cmd.add_element("name", name)
515
516
        if value:
517
            cmd.add_element("value", value)
518
519
        if active is not None:
520
            cmd.add_element("active", _to_bool(active))
521
522
        if resource_action or resource_filter or resource_ids or resource_type:
523
            if not resource_filter and not resource_ids:
524
                raise RequiredArgument(
525
                    "modify_tag requires resource_filter or resource_ids "
526
                    "argument when resource_action or resource_type is set"
527
                )
528
529
            if not resource_type:
530
                raise RequiredArgument(
531
                    "modify_tag requires resource_type argument when "
532
                    "resource_action or resource_filter or resource_ids "
533
                    "is set"
534
                )
535
536
            _xmlresources = cmd.add_element("resources")
537
            if resource_action is not None:
538
                _xmlresources.set_attribute("action", resource_action)
539
540
            if resource_filter is not None:
541
                _xmlresources.set_attribute("filter", resource_filter)
542
543
            for resource_id in resource_ids or []:
544
                _xmlresources.add_element(
545
                    "resource", attrs={"id": str(resource_id)}
546
                )
547
548
            _xmlresources.add_element("type", resource_type)
549
550
        return self._send_xml_command(cmd)
551