Passed
Pull Request — master (#136)
by
unknown
02:10
created

gvm.protocols.gmpv8.Gmp.modify_credential()   F

Complexity

Conditions 23

Size

Total Lines 123
Code Lines 74

Duplication

Lines 16
Ratio 13.01 %

Importance

Changes 0
Metric Value
cc 23
eloc 74
nop 17
dl 16
loc 123
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like gvm.protocols.gmpv8.Gmp.modify_credential() 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.

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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, redefined-builtin, too-many-lines
20
21
"""
22
Module for communication with gvmd in `Greenbone Management Protocol version 8`_
23
24
.. _Greenbone Management Protocol version 8:
25
    https://docs.greenbone.net/API/GMP/gmp-8.0.html
26
"""
27
from enum import Enum
28
from typing import Any, List, Optional
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, _add_filter
35
36
PROTOCOL_VERSION = (8,)
37
38
39
class FilterType(Enum):
40
    AGENT = "agent"
41
    ALERT = "alert"
42
    ASSET = "asset"
43
    SCAN_CONFIG = "config"
44
    CREDENTIAL = "credential"
45
    FILTER = "filter"
46
    GROUP = "group"
47
    HOST = "host"
48
    NOTE = "note"
49
    OPERATING_SYSTEM = "os"
50
    OVERRIDE = "override"
51
    PERMISSION = "permission"
52
    PORT_LIST = "port_list"
53
    REPORT = "report"
54
    REPORT_FORMAT = "report_format"
55
    RESULT = "result"
56
    ROLE = "role"
57
    SCHEDULE = "schedule"
58
    ALL_SECINFO = "secinfo"
59
    TAG = "tag"
60
    TARGET = "target"
61
    TASK = "task"
62
    TICKET = "ticket"
63
    USER = "user"
64
    VULNERABILITY = "vuln"
65
66
67
def get_filter_type_from_string(
68
    filter_type: Optional[str]
69
) -> Optional[FilterType]:
70
    """ Convert a filter type string to an actual FilterType instance
71
72
    Arguments:
73
        filter_type (str): Filter type string to convert to a FilterType
74
    """
75
    if not filter_type:
76
        return None
77
78
    if filter_type == 'vuln':
79
        return FilterType.VULNERABILITY
80
81
    if filter_type == 'os':
82
        return FilterType.OPERATING_SYSTEM
83
84
    if filter_type == 'config':
85
        return FilterType.SCAN_CONFIG
86
87
    if filter_type == 'secinfo':
88
        return FilterType.ALL_SECINFO
89
90
    try:
91
        return FilterType[filter_type.upper()]
92
    except KeyError:
93
        raise InvalidArgument(
94
            argument='filter_type',
95
            function=get_filter_type_from_string.__name__,
96
        )
97
98
99
class CredentialType(Enum):
100
    CLIENT_CERTIFICATE = 'cc'
101
    SNMP = 'snmp'
102
    USERNAME_PASSWORD = 'up'
103
    USERNAME_SSH_KEY = 'usk'
104
    SMIME_CERTIFICATE = 'smime'
105
    PGP_ENCRYPTION_KEY = 'pgp'
106
    PASSWORD_ONLY = 'pw'
107
108
109
def get_credential_type_from_string(
110
    credential_type: Optional[str]
111
) -> Optional[CredentialType]:
112
    """ Convert a credential type string into a CredentialType instance
113
    """
114
    if not credential_type:
115
        return None
116
117
    try:
118
        return CredentialType[credential_type.upper()]
119
    except KeyError:
120
        raise InvalidArgument(
121
            argument='credential_type',
122
            function=get_credential_type_from_string.__name__,
123
        )
124
125
126
class TicketStatus(Enum):
127
    OPEN = 'Open'
128
    FIXED = 'Fixed'
129
    CLOSED = 'Closed'
130
131
132
def get_ticket_status_from_string(
133
    ticket_status: Optional[str]
134
) -> Optional[TicketStatus]:
135
    """ Convert a ticket status string into a TicketStatus instance
136
    """
137
    if not ticket_status:
138
        return None
139
140
    try:
141
        return TicketStatus[ticket_status.upper()]
142
    except KeyError:
143
        raise InvalidArgument(
144
            argument='ticket_status',
145
            function=get_ticket_status_from_string.__name__,
146
        )
147
148
149
class SnmpAuthAlgorithm(Enum):
150
    SHA1 = 'sha1'
151
    MD5 = 'md5'
152
153
154
def get_snmp_auth_algorithm_from_string(
155
    algorithm: Optional[str]
156
) -> Optional[TicketStatus]:
157
    """ Convert a SNMP auth algorithm string into a SnmpAuthAlgorithm instance
158
    """
159
    if not algorithm:
160
        return None
161
162
    try:
163
        return SnmpAuthAlgorithm[algorithm.upper()]
164
    except KeyError:
165
        raise InvalidArgument(
166
            argument='algorithm',
167
            function=get_snmp_auth_algorithm_from_string.__name__,
168
        )
169
170
171
class SnmpPrivacyAlgorithm(Enum):
172
    AES = 'aes'
173
    DES = 'des'
174
175
176
def get_snmp_privacy_algorithm_from_string(
177
    algorithm: Optional[str]
178
) -> Optional[TicketStatus]:
179
    """ Convert a SNMP privacy algorithm string into a SnmpPrivacyAlgorithm
180
        instance
181
    """
182
    if not algorithm:
183
        return None
184
185
    try:
186
        return SnmpPrivacyAlgorithm[algorithm.upper()]
187
    except KeyError:
188
        raise InvalidArgument(
189
            argument='algorithm',
190
            function=get_snmp_privacy_algorithm_from_string.__name__,
191
        )
192
193
194
class AliveTest(Enum):
195
    ICMP_PING = 'ICMP Ping'
196
    TCP_ACK_SERVICE_PING = 'TCP-ACK Service Ping'
197
    TCP_SYN_SERVICE_PING = 'TCP-SYN Service Ping'
198
    APR_PING = 'ARP Ping'
199
    ICMP_AND_TCP_ACK_SERVICE_PING = 'ICMP & TCP-ACK Service Ping'
200
    ICMP_AND_ARP_PING = 'ICMP & ARP Ping'
201
    TCP_ACK_SERVICE_AND_ARP_PING = 'TCP-ACK Service & ARP Ping'
202
    ICMP_TCP_ACK_SERVICE_AND_ARP_PING = (  # pylint: disable=invalid-name
203
        'ICMP, TCP-ACK Service & ARP Ping'
204
    )
205
    CONSIDER_ALIVE = 'Consider Alive'
206
207
208
class Gmp(Gmpv7):
209
    @staticmethod
210
    def get_protocol_version() -> str:
211
        """Determine the Greenbone Management Protocol version.
212
213
        Returns:
214
            str: Implemented version of the Greenbone Management Protocol
215
        """
216
        return get_version_string(PROTOCOL_VERSION)
217
218
    def create_credential(
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (19/15).
Loading history...
219
        self,
220
        name: str,
221
        credential_type: CredentialType,
222
        *,
223
        comment: Optional[str] = None,
224
        allow_insecure: Optional[bool] = None,
225
        certificate: Optional[str] = None,
226
        key_phrase: Optional[str] = None,
227
        private_key: Optional[str] = None,
228
        login: Optional[str] = None,
229
        password: Optional[str] = None,
230
        auth_algorithm: Optional[SnmpAuthAlgorithm] = None,
231
        community: Optional[str] = None,
232
        privacy_algorithm: Optional[SnmpPrivacyAlgorithm] = None,
233
        privacy_password: Optional[str] = None,
234
        public_key: Optional[str] = None
235
    ) -> Any:
236
        """Create a new credential
237
238
        Create a new credential e.g. to be used in the method of an alert.
239
240
        Currently the following credential types are supported:
241
242
            - Username + Password
243
            - Username + SSH-Key
244
            - Client Certificates
245
            - SNMPv1 or SNMPv2c protocol
246
            - S/MIME Certificate
247
            - OpenPGP Key
248
            - Password only
249
250
        Arguments:
251
            name: Name of the new credential
252
            credential_type: The credential type.
253
            comment: Comment for the credential
254
            allow_insecure: Whether to allow insecure use of the credential
255
            certificate: Certificate for the credential.
256
                Required for client-certificate and smime credential types.
257
            key_phrase: Key passphrase for the private key.
258
                Used for the username+ssh-key credential type.
259
            private_key: Private key to use for login. Required
260
                for usk credential type. Also used for the cc credential type.
261
                The supported key types (dsa, rsa, ecdsa, ...) and formats (PEM,
262
                PKC#12, OpenSSL, ...) depend on your installed GnuTLS version.
263
            login: Username for the credential. Required for username+password,
264
                username+ssh-key and snmp credential type.
265
            password: Password for the credential. Used for username+password
266
                and snmp credential types.
267
            community: The SNMP community
268
            auth_algorithm: The SNMP authentication algorithm. Required for snmp
269
                credential type.
270
            privacy_algorithm: The SNMP privacy algorithm
271
            privacy_password: The SNMP privacy password
272
            public_key: PGP public key in *armor* plain text format. Required
273
                for pgp credential type.
274
275
        Examples:
276
            Creating a Username + Password credential
277
278
            .. code-block:: python
279
280
                gmp.create_credential(
281
                    name='UP Credential',
282
                    credential_type=CredentialType.USERNAME_PASSWORD,
283
                    login='foo',
284
                    password='bar',
285
                );
286
287
            Creating a Username + SSH Key credential
288
289
            .. code-block:: python
290
291
                with open('path/to/private-ssh-key') as f:
292
                    key = f.read()
293
294
                gmp.create_credential(
295
                    name='USK Credential',
296
                    credential_type=CredentialType.USERNAME_SSH_KEY,
297
                    login='foo',
298
                    key_phrase='foobar',
299
                    private_key=key,
300
                )
301
302
            Creating a PGP credential
303
304
            .. note::
305
306
                A compatible public pgp key file can be exported with GnuPG via
307
                ::
308
309
                    $ gpg --armor --export [email protected] > alice.asc
310
311
            .. code-block:: python
312
313
                with open('path/to/pgp.key.asc') as f:
314
                    key = f.read()
315
316
                gmp.create_credential(
317
                    name='PGP Credential',
318
                    credential_type=CredentialType.PGP_ENCRYPTION_KEY,
319
                    public_key=key,
320
                )
321
322
            Creating a S/MIME credential
323
324
            .. code-block:: python
325
326
                with open('path/to/smime-cert') as f:
327
                    cert = f.read()
328
329
                gmp.create_credential(
330
                    name='SMIME Credential',
331
                    credential_type=CredentialType.SMIME_CERTIFICATE,
332
                    certificate=cert,
333
                )
334
335
            Creating a Password-Only credential
336
337
            .. code-block:: python
338
339
                gmp.create_credential(
340
                    name='Password-Only Credential',
341
                    credential_type=CredentialType.PASSWORD_ONLY,
342
                    password='foo',
343
                )
344
        Returns:
345
            The response. See :py:meth:`send_command` for details.
346
        """
347
        if not name:
348
            raise RequiredArgument(
349
                function="create_credential", argument="name"
350
            )
351
352
        if not isinstance(credential_type, CredentialType):
353
            raise InvalidArgument(
354
                "create_credential requires type to be a CredentialType "
355
                "instance",
356
                function="create_credential",
357
                argument="credential_type",
358
            )
359
360
        cmd = XmlCommand("create_credential")
361
        cmd.add_element("name", name)
362
363
        cmd.add_element("type", credential_type.value)
364
365
        if comment:
366
            cmd.add_element("comment", comment)
367
368
        if allow_insecure is not None:
369
            cmd.add_element("allow_insecure", _to_bool(allow_insecure))
370
371
        if (
372
            credential_type == CredentialType.CLIENT_CERTIFICATE
0 ignored issues
show
Unused Code introduced by
Consider merging these comparisons with "in" to 'credential_type in (CredentialType.CLIENT_CERTIFICATE, CredentialType.SMIME_CERTIFICATE)'
Loading history...
373
            or credential_type == CredentialType.SMIME_CERTIFICATE
374
        ):
375
            if not certificate:
376
                raise RequiredArgument(
377
                    "create_credential requires certificate argument for "
378
                    "credential_type {0}".format(credential_type.name),
379
                    function="create_credential",
380
                    argument="certificate",
381
                )
382
383
            cmd.add_element("certificate", certificate)
384
385
        if (
386
            credential_type == CredentialType.USERNAME_PASSWORD
0 ignored issues
show
Unused Code introduced by
Consider merging these comparisons with "in" to 'credential_type in (CredentialType.USERNAME_PASSWORD, CredentialType.USERNAME_SSH_KEY, CredentialType.SNMP)'
Loading history...
387
            or credential_type == CredentialType.USERNAME_SSH_KEY
388
            or credential_type == CredentialType.SNMP
389
        ):
390
            if not login:
391
                raise RequiredArgument(
392
                    "create_credential requires login argument for "
393
                    "credential_type {0}".format(credential_type.name),
394
                    function="create_credential",
395
                    argument="login",
396
                )
397
398
            cmd.add_element("login", login)
399
400
        if credential_type == CredentialType.PASSWORD_ONLY and not password:
401
            raise RequiredArgument(
402
                "create_credential requires password argument for "
403
                "credential_type {0}".format(credential_type.name),
404
                function="create_credential",
405
                argument="password",
406
            )
407
408
        if (
409
            credential_type == CredentialType.USERNAME_PASSWORD
0 ignored issues
show
Unused Code introduced by
Consider merging these comparisons with "in" to 'credential_type in (CredentialType.USERNAME_PASSWORD, CredentialType.SNMP, CredentialType.PASSWORD_ONLY)'
Loading history...
410
            or credential_type == CredentialType.SNMP
411
            or credential_type == CredentialType.PASSWORD_ONLY
412
        ) and password:
413
            cmd.add_element("password", password)
414
415
        if credential_type == CredentialType.USERNAME_SSH_KEY:
416
            if not private_key:
417
                raise RequiredArgument(
418
                    "create_credential requires private_key argument for "
419
                    "credential_type {0}".format(credential_type.name),
420
                    function="create_credential",
421
                    argument="private_key",
422
                )
423
424
            _xmlkey = cmd.add_element("key")
425
            _xmlkey.add_element("private", private_key)
426
427
            if key_phrase:
428
                _xmlkey.add_element("phrase", key_phrase)
429
430
        if credential_type == CredentialType.CLIENT_CERTIFICATE and private_key:
431
            _xmlkey = cmd.add_element("key")
432
            _xmlkey.add_element("private", private_key)
433
434
        if credential_type == CredentialType.SNMP:
435
            if not isinstance(auth_algorithm, SnmpAuthAlgorithm):
436
                raise InvalidArgument(
437
                    "create_credential requires auth_algorithm to be a "
438
                    "SnmpAuthAlgorithm instance",
439
                    function="create_credential",
440
                    argument="auth_algorithm",
441
                )
442
443
            cmd.add_element("auth_algorithm", auth_algorithm.value)
444
445
            if community:
446
                cmd.add_element("community", community)
447
448 View Code Duplication
            if privacy_algorithm is not None or privacy_password:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
449
                _xmlprivacy = cmd.add_element("privacy")
450
451
                if privacy_algorithm is not None:
452
                    if not isinstance(privacy_algorithm, SnmpPrivacyAlgorithm):
453
                        raise InvalidArgument(
454
                            "create_credential requires algorithm to be a "
455
                            "SnmpPrivacyAlgorithm instance",
456
                            function="create_credential",
457
                            argument="privacy_algorithm",
458
                        )
459
460
                    _xmlprivacy.add_element(
461
                        "algorithm", privacy_algorithm.value
462
                    )
463
464
                if privacy_password:
465
                    _xmlprivacy.add_element("password", privacy_password)
466
467
        if credential_type == CredentialType.PGP_ENCRYPTION_KEY:
468
            if not public_key:
469
                raise RequiredArgument(
470
                    "Creating a pgp credential requires a public_key argument",
471
                    argument="public_key",
472
                    function="create_credential",
473
                )
474
475
            _xmlkey = cmd.add_element("key")
476
            _xmlkey.add_element("public", public_key)
477
478
        return self._send_xml_command(cmd)
479
480
    def modify_credential(
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (20/15).
Loading history...
481
        self,
482
        credential_id: str,
483
        *,
484
        name: Optional[str] = None,
485
        comment: Optional[str] = None,
486
        allow_insecure: Optional[bool] = None,
487
        certificate: Optional[str] = None,
488
        key_phrase: Optional[str] = None,
489
        private_key: Optional[str] = None,
490
        login: Optional[str] = None,
491
        password: Optional[str] = None,
492
        auth_algorithm: Optional[SnmpAuthAlgorithm] = None,
493
        community: Optional[str] = None,
494
        privacy_algorithm: Optional[SnmpPrivacyAlgorithm] = None,
495
        privacy_password: Optional[str] = None,
496
        credential_type: Optional[CredentialType] = None,
497
        public_key: Optional[str] = None
498
    ) -> Any:
499
        """Modifies an existing credential.
500
501
        Arguments:
502
            credential_id: UUID of the credential
503
            name: Name of the credential
504
            comment: Comment for the credential
505
            allow_insecure: Whether to allow insecure use of the credential
506
            certificate: Certificate for the credential
507
            key_phrase: Key passphrase for the private key
508
            private_key: Private key to use for login
509
            login: Username for the credential
510
            password: Password for the credential
511
            auth_algorithm: The authentication algorithm for SNMP
512
            community: The SNMP community
513
            privacy_algorithm: The privacy algorithm for SNMP
514
            privacy_password: The SNMP privacy password
515
            credential_type: The credential type.
516
            public_key: PGP public key in *armor* plain text format
517
518
        Returns:
519
            The response. See :py:meth:`send_command` for details.
520
        """
521
        if not credential_id:
522
            raise RequiredArgument(
523
                argument="credential_id", function="modify_credential"
524
            )
525
526
        cmd = XmlCommand("modify_credential")
527
        cmd.set_attribute("credential_id", credential_id)
528
529
        if credential_type:
530
            if not isinstance(credential_type, CredentialType):
531
                raise InvalidArgument(
532
                    "modify_credential requires type to be a CredentialType "
533
                    "instance",
534
                    argument="type",
535
                    function="modify_credential",
536
                )
537
            cmd.add_element("type", credential_type.value)
538
539
        if comment:
540
            cmd.add_element("comment", comment)
541
542
        if name:
543
            cmd.add_element("name", name)
544
545
        if allow_insecure is not None:
546
            cmd.add_element("allow_insecure", _to_bool(allow_insecure))
547
548
        if certificate:
549
            cmd.add_element("certificate", certificate)
550
551
        if key_phrase or private_key:
552
            if not key_phrase or not private_key:
553
                raise RequiredArgument(
554
                    "modify_credential requires "
555
                    "a key_phrase and private_key arguments",
556
                    function="modify_credential",
557
                )
558
            _xmlkey = cmd.add_element("key")
559
            _xmlkey.add_element("phrase", key_phrase)
560
            _xmlkey.add_element("private", private_key)
561
562
        if login:
563
            cmd.add_element("login", login)
564
565
        if password:
566
            cmd.add_element("password", password)
567
568
        if auth_algorithm:
569
            if not isinstance(auth_algorithm, SnmpAuthAlgorithm):
570
                raise InvalidArgument(
571
                    "modify_credential requires auth_algorithm to be a "
572
                    "SnmpAuthAlgorithm instance",
573
                    argument="auth_algorithm",
574
                    function="modify_credential",
575
                )
576
            cmd.add_element("auth_algorithm", auth_algorithm.value)
577
578
        if community:
579
            cmd.add_element("community", community)
580
581 View Code Duplication
        if privacy_algorithm is not None or privacy_password is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
582
            _xmlprivacy = cmd.add_element("privacy")
583
584
            if privacy_algorithm is not None:
585
                if not isinstance(privacy_algorithm, SnmpPrivacyAlgorithm):
586
                    raise InvalidArgument(
587
                        "modify_credential requires privacy_algorithm to be "
588
                        "a SnmpPrivacyAlgorithm instance",
589
                        argument="privacy_algorithm",
590
                        function="modify_credential",
591
                    )
592
593
                _xmlprivacy.add_element("algorithm", privacy_algorithm.value)
594
595
            if privacy_password is not None:
596
                _xmlprivacy.add_element("password", privacy_password)
597
598
        if public_key:
599
            _xmlkey = cmd.add_element("key")
600
            _xmlkey.add_element("public", public_key)
601
602
        return self._send_xml_command(cmd)
603
604
    def create_tag(
605
        self,
606
        name: str,
607
        resource_type: str,
608
        *,
609
        resource_filter: Optional[str] = None,
610
        resource_ids: Optional[List[str]] = None,
611
        value: Optional[str] = None,
612
        comment: Optional[str] = None,
613
        active: Optional[bool] = None
614
    ) -> Any:
615
        """Create a tag.
616
617
        Arguments:
618
            name: Name of the tag. A full tag name consisting of namespace and
619
                predicate e.g. `foo:bar`.
620
            resource_type: Entity type the tag is to be attached to.
621
            resource_filter: Filter term to select resources the tag is to be
622
                attached to. Only one of resource_filter or resource_ids can be
623
                provided.
624
            resource_ids: IDs of the resources the tag is to be attached to.
625
                Only one of resource_filter or resource_ids can be provided.
626
            value: Value associated with the tag.
627
            comment: Comment for the tag.
628
            active: Whether the tag should be active.
629
630
        Returns:
631
            The response. See :py:meth:`send_command` for details.
632
        """
633
        if not name:
634
            raise RequiredArgument(function="create_tag", argument="name")
635
636
        if resource_filter and resource_ids:
637
            raise InvalidArgument(
638
                "create_tag accepts either resource_filter or resource_ids "
639
                "argument",
640
                function="create_tag",
641
            )
642
643
        if not resource_type:
644
            raise RequiredArgument(
645
                function="create_tag", argument="resource_type"
646
            )
647
648
        cmd = XmlCommand('create_tag')
649
        cmd.add_element('name', name)
650
651
        _xmlresources = cmd.add_element("resources")
652
        if resource_filter is not None:
653
            _xmlresources.set_attribute("filter", resource_filter)
654
655
        for resource_id in resource_ids or []:
656
            _xmlresources.add_element(
657
                "resource", attrs={"id": str(resource_id)}
658
            )
659
660
        _xmlresources.add_element("type", resource_type)
661
662
        if comment:
663
            cmd.add_element("comment", comment)
664
665
        if value:
666
            cmd.add_element("value", value)
667
668
        if active is not None:
669
            if active:
670
                cmd.add_element("active", "1")
671
            else:
672
                cmd.add_element("active", "0")
673
674
        return self._send_xml_command(cmd)
675
676
    def modify_tag(
677
        self,
678
        tag_id: str,
679
        *,
680
        comment: Optional[str] = None,
681
        name: Optional[str] = None,
682
        value=None,
683
        active=None,
684
        resource_action: Optional[str] = None,
685
        resource_type: Optional[str] = None,
686
        resource_filter: Optional[str] = None,
687
        resource_ids: Optional[List[str]] = None
688
    ) -> Any:
689
        """Modifies an existing tag.
690
691
        Arguments:
692
            tag_id: UUID of the tag.
693
            comment: Comment to add to the tag.
694
            name: Name of the tag.
695
            value: Value of the tag.
696
            active: Whether the tag is active.
697
            resource_action: Whether to add or remove resources instead of
698
                overwriting. One of '', 'add', 'set' or 'remove'.
699
            resource_type: Type of the resources to which to attach the tag.
700
                Required if resource_filter is set.
701
            resource_filter: Filter term to select resources the tag is to be
702
                attached to.
703
            resource_ids: IDs of the resources to which to attach the tag.
704
705
        Returns:
706
            The response. See :py:meth:`send_command` for details.
707
        """
708
        if not tag_id:
709
            raise RequiredArgument("modify_tag requires a tag_id element")
710
711
        cmd = XmlCommand("modify_tag")
712
        cmd.set_attribute("tag_id", str(tag_id))
713
714
        if comment:
715
            cmd.add_element("comment", comment)
716
717
        if name:
718
            cmd.add_element("name", name)
719
720
        if value:
721
            cmd.add_element("value", value)
722
723
        if active is not None:
724
            cmd.add_element("active", _to_bool(active))
725
726
        if resource_action or resource_filter or resource_ids or resource_type:
727
            if resource_filter and not resource_type:
728
                raise RequiredArgument(
729
                    "modify_tag requires resource_type argument when "
730
                    "resource_filter is set",
731
                    function="modify_tag",
732
                    argument="resource_type",
733
                )
734
735
            _xmlresources = cmd.add_element("resources")
736
            if resource_action is not None:
737
                _xmlresources.set_attribute("action", resource_action)
738
739
            if resource_filter is not None:
740
                _xmlresources.set_attribute("filter", resource_filter)
741
742
            for resource_id in resource_ids or []:
743
                _xmlresources.add_element(
744
                    "resource", attrs={"id": str(resource_id)}
745
                )
746
747
            if resource_type is not None:
748
                _xmlresources.add_element("type", resource_type)
749
750
        return self._send_xml_command(cmd)
751
752
    def clone_ticket(self, ticket_id: str) -> Any:
753
        """Clone an existing ticket
754
755
        Arguments:
756
            ticket_id: UUID of an existing ticket to clone from
757
758
        Returns:
759
            The response. See :py:meth:`send_command` for details.
760
        """
761
        if not ticket_id:
762
            raise RequiredArgument(
763
                function="clone_ticket", argument="ticket_id"
764
            )
765
766
        cmd = XmlCommand("create_ticket")
767
768
        _copy = cmd.add_element("copy", ticket_id)
769
770
        return self._send_xml_command(cmd)
771
772
    def create_ticket(
773
        self,
774
        *,
775
        result_id: str,
776
        assigned_to_user_id: str,
777
        note: str,
778
        comment: Optional[str] = None
779
    ) -> Any:
780
        """Create a new ticket
781
782
        Arguments:
783
            result_id: UUID of the result the ticket applies to
784
            assigned_to_user_id: UUID of a user the ticket should be assigned to
785
            note: A note about opening the ticket
786
            comment: Comment for the ticket
787
788
        Returns:
789
            The response. See :py:meth:`send_command` for details.
790
        """
791
        if not result_id:
792
            raise RequiredArgument(
793
                function="create_ticket", argument="result_id"
794
            )
795
796
        if not assigned_to_user_id:
797
            raise RequiredArgument(
798
                function="create_ticket", argument="assigned_to_user_id"
799
            )
800
801
        if not note:
802
            raise RequiredArgument(function="create_ticket", argument="note")
803
804
        cmd = XmlCommand("create_ticket")
805
806
        _result = cmd.add_element("result")
807
        _result.set_attribute("id", result_id)
808
809
        _assigned = cmd.add_element("assigned_to")
810
        _user = _assigned.add_element("user")
811
        _user.set_attribute("id", assigned_to_user_id)
812
813
        _note = cmd.add_element("open_note", note)
814
815
        if comment:
816
            cmd.add_element("comment", comment)
817
818
        return self._send_xml_command(cmd)
819
820
    def delete_ticket(
821
        self, ticket_id: str, *, ultimate: Optional[bool] = False
822
    ):
823
        """Deletes an existing ticket
824
825
        Arguments:
826
            ticket_id: UUID of the ticket to be deleted.
827
            ultimate: Whether to remove entirely, or to the trashcan.
828
        """
829
        if not ticket_id:
830
            raise RequiredArgument(
831
                function="delete_ticket", argument="ticket_id"
832
            )
833
834
        cmd = XmlCommand("delete_ticket")
835
        cmd.set_attribute("ticket_id", ticket_id)
836
        cmd.set_attribute("ultimate", _to_bool(ultimate))
837
838
        return self._send_xml_command(cmd)
839
840
    def get_tickets(
841
        self,
842
        *,
843
        trash: Optional[bool] = None,
844
        filter: Optional[str] = None,
845
        filter_id: Optional[str] = None
846
    ) -> Any:
847
        """Request a list of tickets
848
849
        Arguments:
850
            filter: Filter term to use for the query
851
            filter_id: UUID of an existing filter to use for the query
852
            trash: True to request the tickets in the trashcan
853
854
        Returns:
855
            The response. See :py:meth:`send_command` for details.
856
        """
857
        cmd = XmlCommand("get_tickets")
858
859
        _add_filter(cmd, filter, filter_id)
860
861
        if not trash is None:
862
            cmd.set_attribute("trash", _to_bool(trash))
863
864
        return self._send_xml_command(cmd)
865
866
    def get_ticket(self, ticket_id: str) -> Any:
867
        """Request a single ticket
868
869
        Arguments:
870
            ticket_id: UUID of an existing ticket
871
872
        Returns:
873
            The response. See :py:meth:`send_command` for details.
874
        """
875
        if not ticket_id:
876
            raise RequiredArgument(function="get_ticket", argument="ticket_id")
877
878
        cmd = XmlCommand("get_tickets")
879
        cmd.set_attribute("ticket_id", ticket_id)
880
        return self._send_xml_command(cmd)
881
882
    def get_vulnerabilities(
883
        self, *, filter: Optional[str] = None, filter_id: Optional[str] = None
884
    ) -> Any:
885
        """Request a list of vulnerabilities
886
887
        Arguments:
888
            filter: Filter term to use for the query
889
            filter_id: UUID of an existing filter to use for the query
890
        Returns:
891
            The response. See :py:meth:`send_command` for details.
892
        """
893
        cmd = XmlCommand("get_vulns")
894
895
        _add_filter(cmd, filter, filter_id)
896
897
        return self._send_xml_command(cmd)
898
899
    def get_vulnerability(self, vulnerability_id: str) -> Any:
900
        """Request a single vulnerability
901
902
        Arguments:
903
            vulnerability_id: ID of an existing vulnerability
904
905
        Returns:
906
            The response. See :py:meth:`send_command` for details.
907
        """
908
        if not vulnerability_id:
909
            raise RequiredArgument(
910
                function="get_vulnerability", argument="vulnerability_id"
911
            )
912
913
        cmd = XmlCommand("get_vulns")
914
        cmd.set_attribute("vuln_id", vulnerability_id)
915
        return self._send_xml_command(cmd)
916
917
    def modify_ticket(
918
        self,
919
        ticket_id: str,
920
        *,
921
        status: Optional[TicketStatus] = None,
922
        note: Optional[str] = None,
923
        assigned_to_user_id: Optional[str] = None,
924
        comment: Optional[str] = None
925
    ) -> Any:
926
        """Modify a single ticket
927
928
        Arguments:
929
            ticket_id: UUID of an existing ticket
930
            status: New status for the ticket
931
            note: Note for the status change. Required if status is set.
932
            assigned_to_user_id: UUID of the user the ticket should be assigned
933
                to
934
            comment: Comment for the ticket
935
936
        Returns:
937
            The response. See :py:meth:`send_command` for details.
938
        """
939
        if not ticket_id:
940
            raise RequiredArgument(
941
                function="modify_ticket", argument="ticket_id"
942
            )
943
944
        if status and not note:
945
            raise RequiredArgument(
946
                "setting a status in modify_ticket requires a note argument",
947
                function="modify_ticket",
948
                argument="note",
949
            )
950
951
        if note and not status:
952
            raise RequiredArgument(
953
                "setting a note in modify_ticket requires a status argument",
954
                function="modify_ticket",
955
                argument="status",
956
            )
957
958
        cmd = XmlCommand("modify_ticket")
959
        cmd.set_attribute("ticket_id", ticket_id)
960
961
        if assigned_to_user_id:
962
            _assigned = cmd.add_element("assigned_to")
963
            _user = _assigned.add_element("user")
964
            _user.set_attribute("id", assigned_to_user_id)
965
966
        if status:
967
            if not isinstance(status, TicketStatus):
968
                raise InvalidArgument(
969
                    "status argument of modify_ticket needs to be a "
970
                    "TicketStatus",
971
                    function="modify_ticket",
972
                    argument="status",
973
                )
974
975
            cmd.add_element('status', status.value)
976
            cmd.add_element('{}_note'.format(status.name.lower()), note)
977
978
        if comment:
979
            cmd.add_element("comment", comment)
980
981
        return self._send_xml_command(cmd)
982
983 View Code Duplication
    def create_filter(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
984
        self,
985
        name: str,
986
        *,
987
        filter_type: Optional[FilterType] = None,
988
        comment: Optional[str] = None,
989
        term: Optional[str] = None
990
    ) -> Any:
991
        """Create a new filter
992
993
        Arguments:
994
            name: Name of the new filter
995
            filter_type: Entity type for the new filter
996
            comment: Comment for the filter
997
            term: Filter term e.g. 'name=foo'
998
999
        Returns:
1000
            The response. See :py:meth:`send_command` for details.
1001
        """
1002
        if not name:
1003
            raise RequiredArgument(function="create_filter", argument="name")
1004
1005
        cmd = XmlCommand("create_filter")
1006
        _xmlname = cmd.add_element("name", name)
1007
1008
        if comment:
1009
            cmd.add_element("comment", comment)
1010
1011
        if term:
1012
            cmd.add_element("term", term)
1013
1014
        if filter_type:
1015
            if not isinstance(filter_type, FilterType):
1016
                raise InvalidArgument(
1017
                    "create_filter requires type to be a FilterType instance. "
1018
                    "was {}".format(filter_type),
1019
                    function="create_filter",
1020
                    argument="filter_type",
1021
                )
1022
1023
            cmd.add_element("type", filter_type.value)
1024
1025
        return self._send_xml_command(cmd)
1026
1027 View Code Duplication
    def modify_filter(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1028
        self,
1029
        filter_id: str,
1030
        *,
1031
        comment: Optional[str] = None,
1032
        name: Optional[str] = None,
1033
        term: Optional[str] = None,
1034
        filter_type: Optional[FilterType] = None
1035
    ) -> Any:
1036
        """Modifies an existing filter.
1037
1038
        Arguments:
1039
            filter_id: UUID of the filter to be modified
1040
            comment: Comment on filter.
1041
            name: Name of filter.
1042
            term: Filter term.
1043
            filter_type: Resource type filter applies to.
1044
1045
        Returns:
1046
            The response. See :py:meth:`send_command` for details.
1047
        """
1048
        if not filter_id:
1049
            raise RequiredArgument(
1050
                function="modify_filter", argument="filter_id"
1051
            )
1052
1053
        cmd = XmlCommand("modify_filter")
1054
        cmd.set_attribute("filter_id", filter_id)
1055
1056
        if comment:
1057
            cmd.add_element("comment", comment)
1058
1059
        if name:
1060
            cmd.add_element("name", name)
1061
1062
        if term:
1063
            cmd.add_element("term", term)
1064
1065
        if filter_type:
1066
            if not isinstance(filter_type, FilterType):
1067
                raise InvalidArgument(
1068
                    "modify_filter requires type to be a FilterType instance. "
1069
                    "was {}".format(filter_type),
1070
                    function="modify_filter",
1071
                    argument="filter_type",
1072
                )
1073
            cmd.add_element("type", filter_type.value)
1074
1075
        return self._send_xml_command(cmd)
1076
1077
    def create_target(
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (17/15).
Loading history...
1078
        self,
1079
        name: str,
1080
        *,
1081
        asset_hosts_filter: Optional[str] = None,
1082
        hosts: Optional[List[str]] = None,
1083
        comment: Optional[str] = None,
1084
        exclude_hosts: Optional[List[str]] = None,
1085
        ssh_credential_id: Optional[str] = None,
1086
        ssh_credential_port: Optional[int] = None,
1087
        smb_credential_id: Optional[str] = None,
1088
        esxi_credential_id: Optional[str] = None,
1089
        snmp_credential_id: Optional[str] = None,
1090
        alive_test: Optional[AliveTest] = None,
1091
        reverse_lookup_only: Optional[bool] = None,
1092
        reverse_lookup_unify: Optional[bool] = None,
1093
        port_range: Optional[str] = None,
1094
        port_list_id: Optional[str] = None
1095
    ) -> Any:
1096
        """Create a new target
1097
1098
        Arguments:
1099
            name: Name of the target
1100
            asset_hosts_filter: Filter to select target host from assets hosts
1101
            hosts: List of hosts addresses to scan
1102
            exclude_hosts: List of hosts addresses to exclude from scan
1103
            comment: Comment for the target
1104
            ssh_credential_id: UUID of a ssh credential to use on target
1105
            ssh_credential_port: The port to use for ssh credential
1106
            smb_credential_id: UUID of a smb credential to use on target
1107
            snmp_credential_id: UUID of a snmp credential to use on target
1108
            esxi_credential_id: UUID of a esxi credential to use on target
1109
            alive_test: Which alive tests to use
1110
            reverse_lookup_only: Whether to scan only hosts that have names
1111
            reverse_lookup_unify: Whether to scan only one IP when multiple IPs
1112
                have the same name.
1113
            port_range: Port range for the target
1114
            port_list_id: UUID of the port list to use on target
1115
1116
        Returns:
1117
            The response. See :py:meth:`send_command` for details.
1118
        """
1119
        if alive_test:
1120
            if not isinstance(alive_test, AliveTest):
1121
                raise InvalidArgument(
1122
                    argument='alive_tests', function='create_target'
1123
                )
1124
            alive_test = alive_test.value
1125
1126
        return super().create_target(
1127
            name,
1128
            asset_hosts_filter=asset_hosts_filter,
1129
            hosts=hosts,
1130
            exclude_hosts=exclude_hosts,
1131
            comment=comment,
1132
            ssh_credential_id=ssh_credential_id,
1133
            ssh_credential_port=ssh_credential_port,
1134
            smb_credential_id=smb_credential_id,
1135
            snmp_credential_id=snmp_credential_id,
1136
            esxi_credential_id=esxi_credential_id,
1137
            alive_tests=alive_test,
1138
            reverse_lookup_only=reverse_lookup_only,
1139
            reverse_lookup_unify=reverse_lookup_unify,
1140
            port_range=port_range,
1141
            port_list_id=port_list_id,
1142
        )
1143