CredentialsMixin.get_credentials()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 36
Code Lines 17

Duplication

Lines 36
Ratio 100 %

Importance

Changes 0
Metric Value
cc 4
eloc 17
nop 7
dl 36
loc 36
rs 9.55
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2021 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
20
from enum import Enum
21
from typing import Any, Optional
22
23
from gvm.errors import InvalidArgument, InvalidArgumentType, RequiredArgument
24
from gvm.utils import add_filter, to_bool
25
from gvm.xml import XmlCommand
26
27
28
class CredentialFormat(Enum):
29
    """Enum for credential format"""
30
31
    KEY = 'key'
32
    RPM = 'rpm'
33
    DEB = 'deb'
34
    EXE = 'exe'
35
    PEM = 'pem'
36
37
38
def get_credential_format_from_string(
39
    credential_format: Optional[str],
40
) -> Optional[CredentialFormat]:
41
    if not credential_format:
42
        return None
43
44
    try:
45
        return CredentialFormat[credential_format.upper()]
46
    except KeyError:
47
        raise InvalidArgument(
48
            argument='credential_format',
49
            function=get_credential_format_from_string.__name__,
50
        ) from None
51
52
53
class CredentialType(Enum):
54
    """Enum for credential types"""
55
56
    CLIENT_CERTIFICATE = 'cc'
57
    SNMP = 'snmp'
58
    USERNAME_PASSWORD = 'up'
59
    USERNAME_SSH_KEY = 'usk'
60
    SMIME_CERTIFICATE = 'smime'
61
    PGP_ENCRYPTION_KEY = 'pgp'
62
    PASSWORD_ONLY = 'pw'
63
64
65
def get_credential_type_from_string(
66
    credential_type: Optional[str],
67
) -> Optional[CredentialType]:
68
    """Convert a credential type string into a CredentialType instance"""
69
    if not credential_type:
70
        return None
71
72
    try:
73
        return CredentialType[credential_type.upper()]
74
    except KeyError:
75
        raise InvalidArgument(
76
            argument='credential_type',
77
            function=get_credential_type_from_string.__name__,
78
        ) from None
79
80
81
class SnmpAuthAlgorithm(Enum):
82
    """Enum for SNMP auth algorithm"""
83
84
    SHA1 = 'sha1'
85
    MD5 = 'md5'
86
87
88
def get_snmp_auth_algorithm_from_string(
89
    algorithm: Optional[str],
90
) -> Optional[SnmpAuthAlgorithm]:
91
    """Convert a SNMP auth algorithm string into a SnmpAuthAlgorithm instance"""
92
    if not algorithm:
93
        return None
94
95
    try:
96
        return SnmpAuthAlgorithm[algorithm.upper()]
97
    except KeyError:
98
        raise InvalidArgument(
99
            argument='algorithm',
100
            function=get_snmp_auth_algorithm_from_string.__name__,
101
        ) from None
102
103
104
class SnmpPrivacyAlgorithm(Enum):
105
    """Enum for SNMP privacy algorithm"""
106
107
    AES = 'aes'
108
    DES = 'des'
109
110
111
def get_snmp_privacy_algorithm_from_string(
112
    algorithm: Optional[str],
113
) -> Optional[SnmpPrivacyAlgorithm]:
114
    """Convert a SNMP privacy algorithm string into a SnmpPrivacyAlgorithm
115
    instance
116
    """
117
    if not algorithm:
118
        return None
119
120
    try:
121
        return SnmpPrivacyAlgorithm[algorithm.upper()]
122
    except KeyError:
123
        raise InvalidArgument(
124
            argument='algorithm',
125
            function=get_snmp_privacy_algorithm_from_string.__name__,
126
        ) from None
127
128
129
class CredentialsMixin:
130
    def clone_credential(self, credential_id: str) -> Any:
131
        """Clone an existing credential
132
133
        Arguments:
134
            credential_id: UUID of an existing credential to clone from
135
136
        Returns:
137
            The response. See :py:meth:`send_command` for details.
138
        """
139
        if not credential_id:
140
            raise RequiredArgument(
141
                function=self.clone_credential.__name__,
142
                argument='credential_id',
143
            )
144
145
        cmd = XmlCommand("create_credential")
146
        cmd.add_element("copy", credential_id)
147
        return self._send_xml_command(cmd)
148
149
    def create_credential(
150
        self,
151
        name: str,
152
        credential_type: CredentialType,
153
        *,
154
        comment: Optional[str] = None,
155
        allow_insecure: Optional[bool] = None,
156
        certificate: Optional[str] = None,
157
        key_phrase: Optional[str] = None,
158
        private_key: Optional[str] = None,
159
        login: Optional[str] = None,
160
        password: Optional[str] = None,
161
        auth_algorithm: Optional[SnmpAuthAlgorithm] = None,
162
        community: Optional[str] = None,
163
        privacy_algorithm: Optional[SnmpPrivacyAlgorithm] = None,
164
        privacy_password: Optional[str] = None,
165
        public_key: Optional[str] = None,
166
    ) -> Any:
167
        """Create a new credential
168
169
        Create a new credential e.g. to be used in the method of an alert.
170
171
        Currently the following credential types are supported:
172
173
            - Username + Password
174
            - Username + SSH-Key
175
            - Client Certificates
176
            - SNMPv1 or SNMPv2c protocol
177
            - S/MIME Certificate
178
            - OpenPGP Key
179
            - Password only
180
181
        Arguments:
182
            name: Name of the new credential
183
            credential_type: The credential type.
184
            comment: Comment for the credential
185
            allow_insecure: Whether to allow insecure use of the credential
186
            certificate: Certificate for the credential.
187
                Required for client-certificate and smime credential types.
188
            key_phrase: Key passphrase for the private key.
189
                Used for the username+ssh-key credential type.
190
            private_key: Private key to use for login. Required
191
                for usk credential type. Also used for the cc credential type.
192
                The supported key types (dsa, rsa, ecdsa, ...) and formats (PEM,
193
                PKC#12, OpenSSL, ...) depend on your installed GnuTLS version.
194
            login: Username for the credential. Required for username+password,
195
                username+ssh-key and snmp credential type.
196
            password: Password for the credential. Used for username+password
197
                and snmp credential types.
198
            community: The SNMP community
199
            auth_algorithm: The SNMP authentication algorithm. Required for snmp
200
                credential type.
201
            privacy_algorithm: The SNMP privacy algorithm
202
            privacy_password: The SNMP privacy password
203
            public_key: PGP public key in *armor* plain text format. Required
204
                for pgp credential type.
205
206
        Examples:
207
            Creating a Username + Password credential
208
209
            .. code-block:: python
210
211
                gmp.create_credential(
212
                    name='UP Credential',
213
                    credential_type=CredentialType.USERNAME_PASSWORD,
214
                    login='foo',
215
                    password='bar',
216
                )
217
218
            Creating a Username + SSH Key credential
219
220
            .. code-block:: python
221
222
                with open('path/to/private-ssh-key') as f:
223
                    key = f.read()
224
225
                gmp.create_credential(
226
                    name='USK Credential',
227
                    credential_type=CredentialType.USERNAME_SSH_KEY,
228
                    login='foo',
229
                    key_phrase='foobar',
230
                    private_key=key,
231
                )
232
233
            Creating a PGP credential
234
235
            .. note::
236
237
                A compatible public pgp key file can be exported with GnuPG via
238
                ::
239
240
                    $ gpg --armor --export [email protected] > alice.asc
241
242
            .. code-block:: python
243
244
                with open('path/to/pgp.key.asc') as f:
245
                    key = f.read()
246
247
                gmp.create_credential(
248
                    name='PGP Credential',
249
                    credential_type=CredentialType.PGP_ENCRYPTION_KEY,
250
                    public_key=key,
251
                )
252
253
            Creating a S/MIME credential
254
255
            .. code-block:: python
256
257
                with open('path/to/smime-cert') as f:
258
                    cert = f.read()
259
260
                gmp.create_credential(
261
                    name='SMIME Credential',
262
                    credential_type=CredentialType.SMIME_CERTIFICATE,
263
                    certificate=cert,
264
                )
265
266
            Creating a Password-Only credential
267
268
            .. code-block:: python
269
270
                gmp.create_credential(
271
                    name='Password-Only Credential',
272
                    credential_type=CredentialType.PASSWORD_ONLY,
273
                    password='foo',
274
                )
275
        Returns:
276
            The response. See :py:meth:`send_command` for details.
277
        """
278
        if not name:
279
            raise RequiredArgument(
280
                function=self.create_credential.__name__, argument='name'
281
            )
282
283
        if not isinstance(credential_type, CredentialType):
284
            raise InvalidArgumentType(
285
                function=self.create_credential.__name__,
286
                argument='credential_type',
287
                arg_type=CredentialType.__name__,
288
            )
289
290
        cmd = XmlCommand("create_credential")
291
        cmd.add_element("name", name)
292
293
        cmd.add_element("type", credential_type.value)
294
295
        if comment:
296
            cmd.add_element("comment", comment)
297
298
        if allow_insecure is not None:
299
            cmd.add_element("allow_insecure", to_bool(allow_insecure))
300
301
        if (
302
            credential_type == CredentialType.CLIENT_CERTIFICATE
303
            or credential_type == CredentialType.SMIME_CERTIFICATE
304
        ):
305
            if not certificate:
306
                raise RequiredArgument(
307
                    function=self.create_credential.__name__,
308
                    argument='certificate',
309
                )
310
311
            cmd.add_element("certificate", certificate)
312
313
        if (
314
            credential_type == CredentialType.USERNAME_PASSWORD
315
            or credential_type == CredentialType.USERNAME_SSH_KEY
316
            or credential_type == CredentialType.SNMP
317
        ):
318
            if not login:
319
                raise RequiredArgument(
320
                    function=self.create_credential.__name__, argument='login'
321
                )
322
323
            cmd.add_element("login", login)
324
325
        if credential_type == CredentialType.PASSWORD_ONLY and not password:
326
            raise RequiredArgument(
327
                function=self.create_credential.__name__, argument='password'
328
            )
329
330
        if (
331
            credential_type == CredentialType.USERNAME_PASSWORD
332
            or credential_type == CredentialType.SNMP
333
            or credential_type == CredentialType.PASSWORD_ONLY
334
        ) and password:
335
            cmd.add_element("password", password)
336
337
        if credential_type == CredentialType.USERNAME_SSH_KEY:
338
            if not private_key:
339
                raise RequiredArgument(
340
                    function=self.create_credential.__name__,
341
                    argument='private_key',
342
                )
343
344
            _xmlkey = cmd.add_element("key")
345
            _xmlkey.add_element("private", private_key)
346
347
            if key_phrase:
348
                _xmlkey.add_element("phrase", key_phrase)
349
350
        if credential_type == CredentialType.CLIENT_CERTIFICATE and private_key:
351
            _xmlkey = cmd.add_element("key")
352
            _xmlkey.add_element("private", private_key)
353
354
        if credential_type == CredentialType.SNMP:
355
            if not isinstance(auth_algorithm, SnmpAuthAlgorithm):
356
                raise InvalidArgumentType(
357
                    function=self.create_credential.__name__,
358
                    argument='auth_algorithm',
359
                    arg_type=SnmpAuthAlgorithm.__name__,
360
                )
361
362
            cmd.add_element("auth_algorithm", auth_algorithm.value)
363
364
            if community:
365
                cmd.add_element("community", community)
366
367 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...
368
                _xmlprivacy = cmd.add_element("privacy")
369
370
                if privacy_algorithm is not None:
371
                    if not isinstance(privacy_algorithm, SnmpPrivacyAlgorithm):
372
                        raise InvalidArgumentType(
373
                            function=self.create_credential.__name__,
374
                            argument='privacy_algorithm',
375
                            arg_type=SnmpPrivacyAlgorithm.__name__,
376
                        )
377
378
                    _xmlprivacy.add_element(
379
                        "algorithm", privacy_algorithm.value
380
                    )
381
382
                if privacy_password:
383
                    _xmlprivacy.add_element("password", privacy_password)
384
385
        if credential_type == CredentialType.PGP_ENCRYPTION_KEY:
386
            if not public_key:
387
                raise RequiredArgument(
388
                    function=self.create_credential.__name__,
389
                    argument='public_key',
390
                )
391
392
            _xmlkey = cmd.add_element("key")
393
            _xmlkey.add_element("public", public_key)
394
395
        return self._send_xml_command(cmd)
396
397
    def delete_credential(
398
        self, credential_id: str, *, ultimate: Optional[bool] = False
399
    ) -> Any:
400
        """Deletes an existing credential
401
402
        Arguments:
403
            credential_id: UUID of the credential to be deleted.
404
            ultimate: Whether to remove entirely, or to the trashcan.
405
        """
406
        if not credential_id:
407
            raise RequiredArgument(
408
                function=self.delete_credential.__name__,
409
                argument='credential_id',
410
            )
411
412
        cmd = XmlCommand("delete_credential")
413
        cmd.set_attribute("credential_id", credential_id)
414
        cmd.set_attribute("ultimate", to_bool(ultimate))
415
416
        return self._send_xml_command(cmd)
417
418 View Code Duplication
    def get_credentials(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
419
        self,
420
        *,
421
        filter_string: Optional[str] = None,
422
        filter_id: Optional[str] = None,
423
        scanners: Optional[bool] = None,
424
        trash: Optional[bool] = None,
425
        targets: Optional[bool] = None,
426
    ) -> Any:
427
        """Request a list of credentials
428
429
        Arguments:
430
            filter_string: Filter term to use for the query
431
            filter_id: UUID of an existing filter to use for the query
432
            scanners: Whether to include a list of scanners using the
433
                credentials
434
            trash: Whether to get the trashcan credentials instead
435
            targets: Whether to include a list of targets using the credentials
436
437
        Returns:
438
            The response. See :py:meth:`send_command` for details.
439
        """
440
        cmd = XmlCommand("get_credentials")
441
442
        add_filter(cmd, filter_string, filter_id)
443
444
        if scanners is not None:
445
            cmd.set_attribute("scanners", to_bool(scanners))
446
447
        if trash is not None:
448
            cmd.set_attribute("trash", to_bool(trash))
449
450
        if targets is not None:
451
            cmd.set_attribute("targets", to_bool(targets))
452
453
        return self._send_xml_command(cmd)
454
455
    def get_credential(
456
        self,
457
        credential_id: str,
458
        *,
459
        scanners: Optional[bool] = None,
460
        targets: Optional[bool] = None,
461
        credential_format: Optional[CredentialFormat] = None,
462
    ) -> Any:
463
        """Request a single credential
464
465
        Arguments:
466
            credential_id: UUID of an existing credential
467
            scanners: Whether to include a list of scanners using the
468
                credentials
469
            targets: Whether to include a list of targets using the credentials
470
            credential_format: One of "key", "rpm", "deb", "exe" or "pem"
471
472
        Returns:
473
            The response. See :py:meth:`send_command` for details.
474
        """
475
        if not credential_id:
476
            raise RequiredArgument(
477
                function=self.get_credential.__name__, argument='credential_id'
478
            )
479
480
        cmd = XmlCommand("get_credentials")
481
        cmd.set_attribute("credential_id", credential_id)
482
483
        if credential_format:
484
            if not isinstance(credential_format, CredentialFormat):
485
                raise InvalidArgumentType(
486
                    function=self.get_credential.__name__,
487
                    argument='credential_format',
488
                    arg_type=CredentialFormat.__name__,
489
                )
490
491
            cmd.set_attribute("format", credential_format.value)
492
493
        if scanners is not None:
494
            cmd.set_attribute("scanners", to_bool(scanners))
495
496
        if targets is not None:
497
            cmd.set_attribute("targets", to_bool(targets))
498
499
        return self._send_xml_command(cmd)
500
501
    def modify_credential(
502
        self,
503
        credential_id: str,
504
        *,
505
        name: Optional[str] = None,
506
        comment: Optional[str] = None,
507
        allow_insecure: Optional[bool] = None,
508
        certificate: Optional[str] = None,
509
        key_phrase: Optional[str] = None,
510
        private_key: Optional[str] = None,
511
        login: Optional[str] = None,
512
        password: Optional[str] = None,
513
        auth_algorithm: Optional[SnmpAuthAlgorithm] = None,
514
        community: Optional[str] = None,
515
        privacy_algorithm: Optional[SnmpPrivacyAlgorithm] = None,
516
        privacy_password: Optional[str] = None,
517
        public_key: Optional[str] = None,
518
    ) -> Any:
519
        """Modifies an existing credential.
520
521
        Arguments:
522
            credential_id: UUID of the credential
523
            name: Name of the credential
524
            comment: Comment for the credential
525
            allow_insecure: Whether to allow insecure use of the credential
526
            certificate: Certificate for the credential
527
            key_phrase: Key passphrase for the private key
528
            private_key: Private key to use for login
529
            login: Username for the credential
530
            password: Password for the credential
531
            auth_algorithm: The authentication algorithm for SNMP
532
            community: The SNMP community
533
            privacy_algorithm: The privacy algorithm for SNMP
534
            privacy_password: The SNMP privacy password
535
            public_key: PGP public key in *armor* plain text format
536
537
        Returns:
538
            The response. See :py:meth:`send_command` for details.
539
        """
540
        if not credential_id:
541
            raise RequiredArgument(
542
                function=self.modify_credential.__name__,
543
                argument='credential_id',
544
            )
545
546
        cmd = XmlCommand("modify_credential")
547
        cmd.set_attribute("credential_id", credential_id)
548
549
        if comment:
550
            cmd.add_element("comment", comment)
551
552
        if name:
553
            cmd.add_element("name", name)
554
555
        if allow_insecure is not None:
556
            cmd.add_element("allow_insecure", to_bool(allow_insecure))
557
558
        if certificate:
559
            cmd.add_element("certificate", certificate)
560
561
        if key_phrase and private_key:
562
            _xmlkey = cmd.add_element("key")
563
            _xmlkey.add_element("phrase", key_phrase)
564
            _xmlkey.add_element("private", private_key)
565
        elif (not key_phrase and private_key) or (
566
            key_phrase and not private_key
567
        ):
568
            raise RequiredArgument(
569
                function=self.modify_credential.__name__,
570
                argument='key_phrase and private_key',
571
            )
572
573
        if login:
574
            cmd.add_element("login", login)
575
576
        if password:
577
            cmd.add_element("password", password)
578
579
        if auth_algorithm:
580
            if not isinstance(auth_algorithm, SnmpAuthAlgorithm):
581
                raise InvalidArgumentType(
582
                    function=self.modify_credential.__name__,
583
                    argument='auth_algorithm',
584
                    arg_type=SnmpAuthAlgorithm.__name__,
585
                )
586
            cmd.add_element("auth_algorithm", auth_algorithm.value)
587
588
        if community:
589
            cmd.add_element("community", community)
590
591 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...
592
            _xmlprivacy = cmd.add_element("privacy")
593
594
            if privacy_algorithm is not None:
595
                if not isinstance(privacy_algorithm, SnmpPrivacyAlgorithm):
596
                    raise InvalidArgumentType(
597
                        function=self.modify_credential.__name__,
598
                        argument='privacy_algorithm',
599
                        arg_type=SnmpPrivacyAlgorithm.__name__,
600
                    )
601
602
                _xmlprivacy.add_element("algorithm", privacy_algorithm.value)
603
604
            if privacy_password is not None:
605
                _xmlprivacy.add_element("password", privacy_password)
606
607
        if public_key:
608
            _xmlkey = cmd.add_element("key")
609
            _xmlkey.add_element("public", public_key)
610
611
        return self._send_xml_command(cmd)
612